makroExcel

Programy w Excelu - samodzielne aplikacje pod MS Excel...

MENU

Najciekawsze działy

Poprawny CSS! Poprawny XHTML 1.0 Strict!

Słowo wstępu

Aplikacja MS Excel dzięki oferowanym formantom, makropoleceniom oraz formularzom, a także możliwości skojarzenia tych elementów z fragmentami procedur napisanych w języku VBA (Visual Basic for Applications) jest elastycznym narzędziem pracy mogącym, w wielu przypadkach z powodzeniem, zastąpić wyspecjalizowane programy. Możliwości tych większość użytkowników nawet nie stara się wykorzystać, a nie jest to wcale takie trudne. Na niniejszej stronie pokazano niektóre praktyczne rozwiązania mogące znaleźć zastosowanie w firmie - nie tylko w ewidencji. Przedstawione przykłady zaczerpnięte są z zakresu pracy autora strony tj. działalności spółdzielni mieszkaniowej.

ZAŁOŻENIA PODSTRONY 'PROGRAMY OD KUCHNI'

 Płyta CD W tym dziale nie zamierzam pisać kursu Visual Basica - jeżeli jesteś zainteresowany tego typu materiałem zapraszam do innej części bieżącej witryny. Można również skorzystać z serwisów tematycznych wskazanych w dziale polecane strony www lub z przedstawionych w dziale literatura propozycji księgarskich. Miałem jednak okazję napisać parę funkcjonujących w praktyce mojej firmy programów, z których kilka przedstawiam w tym serwisie i na podstawie realizowanych w nich funkcji chciałbym przedstawić fragmenty kodu, które uznałem za najbardziej interesujące lub do których sam dochodziłem, ponieważ nie znalazłem satysfakcjonujących podpowiedzi w podręcznikach. Programy w excelu, które napisałem obsługiwane są nie tylko przez mnie i stąd mam własne przemyślenia na temat tzw. intuicyjnej obsługi (elementy, które początkowo sam uznawałem za proste i jasne niekoniecznie takimi były dla osób pierwszy raz stykających się z produktem mych twórczych wysiłków ;)

Opracowania powstały na podstawie mojej wiedzy w tym zakresie.
Jeżeli zauważasz nieścisłości w tekście proszę o kontakt.
Postaram się uwzględnić wszystkie tego typu uwagi - patrz np. uwaga na końcu punktu kopia bezpieczeństwa

Uwagi wstępne


Spostrzeżenia wynikły w trakcie realizacji kolejnych programów VBA i pojawiających się reakcji użytkowników. Omówienie uwag należy więc postrzegać przez pryzmat przedstawianych produktów i specyficznych wymagań, którym muszą sprostać. Nie roszczę sobie prawa do dalej idących uogólnień - na pewno każdy wykonujący programy dla osób trzecich ma własne przemyślenia na ten temat.

Poniżej przedstawiam wyszczególnienie poruszanych zagadnień z ewentualnym wskazaniem, z którego programu pochodzi prezentowany algorytm. Zestawienie nie jest zamknięte i miarę potrzeb, chęci i czasu mogę je uzupełniać o zaproponowane tematy.

Przykładowe zagadnienia / fragmenty algorytmów VBA


Jeżeli po zapoznaniu się z instrukcjami lub prezentacjami omawianych programów nie znajdziecie poniżej podpowiedzi na interesujące was fragmenty algorytmów proszę o kontakt. Uzupełnieniem treści podstrony są tutoriale, w których przedstawiam swego rodzaju przepisy na zastosowanie makropoleceń oraz na wykonanie pełnej aplikacji. Jeden przepis zrealizowany jest na przykładzie pliku VBA REJESTR ZAKUPÓW, drugi na przykładzie programu VBA OKNA EWIDENCJA.

Przykładowe tutoriale


Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Program VBA intuicyjny w obsłudze KLIKNIJ by (roz)winąć

Programy w excelu przedstawiane na tej stronie tworzone w ramach MS Excel to większości różnego rodzaju ewidencje lub aplikacje bazodanowe proste pod względem zastosowanych algorytmów oraz programy wspomagające druk korespondencji wg przyjętych szablonów. Część robocza aplikacji to zestaw arkuszy (w niektórych wypadkach ponad trzydzieści). Dla zapewnienia łatwej obsługi arkusze te warto ukryć, pozostawiając tylko arkusz z wyraźnie opisanym menu. Użytkownik zaglądający nawet przypadkowo do arkusza z tabelą 30 kolumn na 3000 wierszy (np. program OKNA EWIDENCJA) z zasady uzna program za trudny w obsłudze, a widząc jeszcze pozostałe arkusze dotyczące podliczeń do wydruków z tak egzotycznymi funkcjami jak 'Licz jeżeli' i odwołaniami do innych arkuszy może nabrać przekonania, że coś tak skomplikowanego jest jednocześnie nieprzydatne. Dużo lepiej arkusze te ukryć, a obsługiwać je z własnych formularzy. W wymienionym przykładzie pierwsza tabela jest obsługiwana z formularza z dwoma przyciskami ('Szukaj lokalu' i 'Zapisz dane'), druga - z formularza z pojedynczymi przyciskami dotyczącymi każdej z siedmiu nieruchomości i dodatkowego przycisku do wydruku zestawienia zbiorczego. Użytkownik nie chce również dochodzić 'gdzie jest' taka czy inna funkcja programu - w miarę możliwości do każdej z nich dostęp musi być realizowany nie więcej jak dwoma kliknięciami myszy. Należy dokładnie przemyśleć układ menu - najczęściej używane funkcje wprowadzić w jednym/dwóch przyciskach, rzadziej używane w pozostałych, sporadycznie używane schować w elementach graficznych. Jednocześnie należy rozdzielić funkcje odczytu danych - używane najczęściej, od funkcji wprowadzania danych - używanych rzadziej.
menu OKNA EWIDENCJAJako przykład chciałbym wskazać menu programu OKNA EWIDENCJA. Program pracuje na kilkunastu arkuszach, ale użytkownik widzi jeden z rysunkowym menu (wypełniającym cały obszar arkusza) jak na ilustracji. Najczęściej używana przez wszystkich funkcja to 'Szukaj danych mieszkania' - stąd jej położenie w prawym górnym rogu menu (ponieważ piszemy od górnego wiersza to tam użytkownik rozpoczyna poszukiwania ;). Przycisk ten służy tylko do odczytu danych, oraz wydruku tzw. wizytówki lokalu (dla zadających pytania mieszkańców) - nie jest blokowany na hasło. Po użyciu program prosi o podanie adresu (okno input) i w zawsze jednakowym formularzu podaje wynik szukania. Druga najczęściej używana funkcja to 'Wprowadź dane mieszkania' - również górny wiersz. Jest to funkcja z dostępem blokowanym na hasło. Wprowadzanie danych wykonywane jest w formularzu identycznym w układzie jak formularz szukania mieszkania, ale etykiety 'Label' zastąpione zostały oknami tekstowymi 'TextBox', przy czym aktywne są tylko okna dotyczące danego lokalu (właściwość 'Enabled'). Funkcje używane rzadziej tzn. wydruku danych dla nieruchomości - przyciski w dolnym wierszu. W obrębie formularzy etykiety (Label), które nie są wypełnione istotnymi wartościami wykonać niewidocznymi (Visible) lub wypełnić 'neutralnym' tekstem np. w lokalach mieszkalnych osiedla występuje łącznie jedenaście różnych zestawów okiennych oznaczonych różnymi symbolami katalogowymi, przy czym na pojedynczym lokalu występuje od dwóch do pięciu zestawów okiennych - po wyszukaniu adresu etykiety pozostałych zestawów - nie dotyczących zadanego lokalu - wypełnione są symbolem '***' - im mniej danych na wydruku tym jest on czytelniejszy. Przycisk 'Kopii bezpieczeństwa' oraz najbardziej lubiany - 'Końca pracy' są mniejsze i rozmieszczone tak by nie zmniejszać czytelności menu. Grafika w środku ekranu nie jest tylko ozdobą - jest to przycisk zawierający informacje o programie oraz funkcje używane raz do roku - rozdzielczość, zmiana haseł dostępu, zmiana położenia pliku kopii zapasowej i roboczej programu, zmiana cennika okien (używana do obliczeń finansowych w obrębie nieruchomości). W ten sposób uzyskałem menu czytelne i jak sądzę intuicyjne - wytłumaczenie działania programu będącego ewidencją w przybliżeniu 13000 okien na 3000 lokali nie zajęło mi jeszcze więcej jak trzy, cztery minuty, przy czym wielkość jednostki administracyjnej jest bez znaczenia dla stopnia skomplikowania menu.

Dążąc do podsumowania i uogólnienia powyższych wywodów program intuicyjny to taki, w którym:

  • Każda funkcja programu dostępna jest nie więcej niż dwoma kliknięciami myszy,
  • Wyposażony jest w czytelne menu z przemyślaną ilością i wielkością (!) przycisków,
  • Funkcje programu w obrębie przycisków są rozsądnie pogrupowane tzn. do codziennej pracy używane jeden/dwa przyciski,
  • Formularze są jak najbardziej ujednolicone w wyglądzie/układzie danych które zawierają,
  • Funkcje podglądu danych i wydruku dostępne są bez haseł, dla każdego użytkownika,
  • Funkcje wprowadzania danych (dostępne na hasło, obsługiwane przez wyznaczonego pracownika) oddzielone są od ich odczytu,
  • Widoczne jest tylko menu programu - arkusze robocze i arkusze wydruków w trakcie pracy pozostają ukryte

W miarę możliwości pozostałe programy ewidencyjne osiedla starałem sie utrzymać w jednakowym układzie menu tj. cztery przyciski główne, dwa pomocnicze + przycisk graficzny. Wielkość i rozmieszczenie menu bez zmian.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Program VBA wygodny w użytkowaniu KLIKNIJ by (roz)winąć

W uwadze powyżej starałem się wyrazić własny pogląd na intuicyjność programów w kontekście zakresu i układu menu. Zakładając, że ten etap mamy już za sobą można przejść dalej. Sama praca z przedstawianymi programami odbywa się za pośrednictwem i na formularzach, stąd mają one decydujące znaczenie dla oceny aplikacji przez użytkownika, a ten nie będzie chciał używać programu, który uzna za niewygodny. Formularze muszą być czytelne (wygodna w czytaniu czcionka, kolory neutralne dostosowane do długiej pracy, bardziej jaskrawe używane jedynie w uzasadnionych przypadkach), wyposażone w możliwie małą ilość formantów wymagających użycia przez obsługującego program (np. CommandButton, ToggleButton, TextBox itp.), sama praca na formularzu powinna odbywać się bez udziału myszy, a ilość dostępnych formantów powinna być dostosowana do aktualnych potrzeb obsługującego. Formularze w obrębie programu powinny być ujednolicone w układzie. Sam formularz powinien być w miarę możliwości symetryczny co do ilości i rozmieszczenia przycisków i innych formantów. Oczywiście bardziej rygorystyczne wymagania dotyczą programów na których pracuje większa część załogi firmy - zwłaszcza jeżeli nie znajduje ona przyjemności w kontakcie z komputerem ;) Mniej ostre wymagania stawiane są aplikacjom używanym przez pojedyncze osoby (np. jest różnica w układzie formularzy WYKUPU GRUNTÓW, obsługiwanego w mojej firmie przez pięć różnych osób, czy BAZĄ TELEADRESOWĄ obsługiwaną przez dziewięć osób, a programem OKNA WYKONAWSTWO, ułatwiającym i organizującym pracę jedynie na moim stanowisku. Oprócz autora jeszcze może dwie osoby sporadycznie potrzebują danych w nim zawartych).
Jako przykład chciałbym wskazać formularze robocze programu REJESTR WODOMIERZY. Podstawowy formularz służy do odczytu danych i sporządzania karty raportu demontażu/montażu wodomierzy dla konserwatorów.
Raport lokalu Kolory formularza (właściwość BackColor określana dla formularza i oddzielnie dla każdego formantu) mogą być definiowane jako wartości bezwzględne (określenie w kodzie szesnastkowym albo przez wskazanie wybranego koloru z palety) lub w nawiązaniu do ustawień systemowych przyjętych indywidualnie przez użytkownika. Osobiście stosuję samodzielne określanie kolorów bez polegania na ustawieniach systemowych. Stąd spokojny popielaty kolor formularza (UserForm) i przycisków (CommandButton). Kolory etykiet (Label) dobrane jako czarne lub ciemno brązowe (adres). Jedyne bardziej zdecydowane kolory związane są z danymi dla konkretnych wodomierzy ciepłej i zimnej wody - odpowiednio czerwony i niebieski. W tym konkretnym przypadku bardziej jaskrawe kolory są uzasadnione ponieważ nie są jedynie ozdobnikiem, ale niosą ze sobą również pewną informację. Wielkość oraz typ czcionki (Verdana) oraz użycie dużych/małych dobrane są tak by poprawić czytelność formularza - niewskazane w tym przypadku są czcionki z rodziny serif np. Times New Roman. Warto zwrócić uwagę, że w momencie uruchomienia formularza przycisk 'Drukuj zestaw' jest zablokowany (właściwość Enabled = False). Udostępniony zostanie on dopiero w momencie użycia przycisku 'Dołącz do druku' (zdarzenie Click dla jednego przycisku zmienia wartość logiczną właściwości Enabled drugiego przycisku), przy czym jeżeli podany został nieistniejący adres to zostanie wyświetlony odpowiedni komunikat, wszystkie etykiety zawierające zmienne dane zostaną wypełnione symbolem 'XXXXX', a przycisk 'Dołącz do druku' zostanie zablokowany. Na etapie projektowania formularza należy bardzo dokładnie rozważyć możliwe dla niego zdarzenia.
Formularz wprowadzania danych do programu jest bardzo podobny w układzie do formularza odczytu danych.Formularz wprowadzania danych Zmienione zostały jedynie dwa środkowe przyciski oraz na pewnym etapie pracy etykiety (Label) na pola tekstowe (TextBox). Ze względu na wprowadzane dane do obsługi formularza wykorzystywana jest jedynie klawiatura numeryczna (domyślną zawartościa pól tekstowych 'uwagi' jest 'brak uwag' stąd właściwa część klawiatury wykorzystywana jest jedynie w niektórych przypadkach). W tym miejscu zwracam uwagę, że sam układ wodomierzy tj. ciepła, zimna woda, kuchnia i łazienka dostosowany jest do układu stosowanych w mojej firmie formularzy odczytowych. Na żądanie osoby obsługującej program klawisz 'Enter' aktywuje okna tekstowe nie w wierszach, a w kolumnach - wprowadzanie danych jest w tym przypadku zdecydowanie szybsze. Do uzyskania takiego efektu konieczne jest wykorzystanie właściwości 'TabIndex' poszczególnych formantów. Użycie klawisza 'Enter' na ostatnim polu tekstowym przenosi użytkownika na przycisk 'Zapisz dane'. Formularz wprowadzania danych Kliknięcie na nim przenosi do przycisku 'Sprawdź dane'. Z kolei użycie tego klawisza powoduje kolejno: wygaszenie pól tekstowych, powtórne wykonanie szukania po adresie, wyświetlenie wyników szukania w postaci etykiet oraz przeniesienie użytkownika na przycisk 'Następny lokal' (metoda SetFocus). Wszystkie formanty tj. pola tekstowe (TextBox) i etykiety (Label) cały czas znajdowały się na formularzu, ale przez odpowiednie wykorzystanie właściwości 'Visible' nie wszystkie były dla użytkownika widoczne. Wyświetlenie etykiet upodabnia formularz bieżący do formularza raportu mieszkania. Ostatnim formularzem, który chciałbym w tym punkcie omówić jest, pochodzący z tego samego programu, formularz druku listy do umawiania wizyty konserwatorów przez gospodarzy domów. Założenie - gospodarz domu powinien dostać listę adresów w obsługiwanych przez siebie budynkach, z wodomierzami których termin legalizacji upłynął. Lista dla gospodarza Listę tę wypełni umawiając dzień i godzinę wizyty konserwatorów wymieniających wodomierze. Gospodarz domu obsługuje cały budynek (blok niski czteropietrowy) lub wejście (blok wysoki dziesięciopiętrowy lub wieżowiec). Zasoby mieszkaniowe osiedla, na którym pracuję obejmują w tym sensie czterdzieści cztery różne rejony pracy pogrupowane w siedem nieruchomości. Dla uzyskania wszystkich możliwych wydruków konieczna więc byłaby dość duża liczba przycisków - na pewno łamiąca zasadę minimalizacji zakresu formularza. Dla uzyskania wygody obsługi rozmieszczono je z wykorzystaniem formantu 'MultiPage', którego poszczególne zakładki przyporządkowano nieruchomościom. Na poszczególnych zakładkach dostępne są jedynie właściwe dla niej przyciski. Formularz uzupełniony został o wspólne dla wszystkich zakładek okno tekstowe z domyślną wartością ustawioną na rok 2000 i opisane bardzo czytelną etykietą. Elementy graficzne na poszczególnych zakładkach mają za zadanie jedynie upodobnić poszczególne zakładki, które mogą zawierać od trzech do osiemnastu przycisków. Największy formularz nie zawiera grafiki. Tutaj można było chyba lepiej rozwiązać zasadę czytelności formularza i na wszystkich zakładkach zastosować to samo zdjęcie w łagodnych, pastelowych kolorach.

Dążąc do podsumowania i uogólnienia powyższych wywodów program wygodny w użytkowaniu to taki, w którym:

  • Formularze są czytelne,
  • Formularze utrzymane są w neutralnych kolorach. Bardziej zdecydowany kolor może być stosowany jedynie w przypadku gdy niesie to z sobą dodatkową informację,
  • Formularze nie są przeładowane ani elementami obsługi ani ozdobnikami graficznymi,
  • Formularze są w miarę możliwości ujednolicone w obrębie programu,
  • Formularze są symetryczne w układzie przycisków i innych elementów,
  • Formularze mają przemyślane rozwiązania zdarzeń,
  • Elementy formularza nie wykorzystywane w danym momencie są niewidoczne lub zablokowane,
  • Jeżeli formularz musi być wyposażony w większą ilość przycisków to muszą one zostać odpowiednio pogrupowane np. w ramki (Frame) lub elementy stronicowane (MultiPage, TabStrip)
  • Na formularzach stosowane są czcionki tylko sans-serif (Verdana, Arial, Tahoma), dobór dużych i małych liter dostosowany jest do wagi informacji.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Czytelne wydruki z programu VBA KLIKNIJ by (roz)winąć

Omawiane programy w excelu służą głównie celom ewidencyjnym i w pewnym zakresie bazodanowym. Wydruki z programów dotyczą podsumowania danych technicznych lub/i finansowych dla lokali. Z samej natury tego typu pracy wydruki wykonywane są dość regularnie dla celów raportowania, planowania i sprawozdawczości. Należy pamiętać o tym, że dla osób nie obsługujących programu znany on będzie tylko z wydruków. Jeżeli odbiorca długo i zawzięcie będzie się zastanawiał co autor miał na myśli lub będzie zmuszony do przebijania się przez kilometry papieru, by dowiedzieć się np. ile okien w danym budynku jest wymienionych to może unikać kontaktu z naszą produkcją (co czasami może być kuszącą perspektywą ;), a ocena jakości naszego programu będzie niska (to już gorzej).
Bieżący punkt chciałbym omówić korzystając z wydruków udostępnianych przez program OKNA EWIDENCJA. Wydruk przede wszystkim powinien być czytelny, co w przypadku tego typu programów jak omawiane oznacza stabelaryzowanie danych. Dobrze jest przyjąć jednakową tabelę dla wydruku wielu różnych zakresów danych - odbiorca raz zapozna się z układem tabeli, później będzie w niej szukał tylko interesujących go danych. Wymieniony program umożliwia wydruk danych o stopniu zaawansowania wymiany okien dla dowolnej kombinacji spośród stu dwudziestu siedmiu wejść jednostki administracyjnej, w której pracuję. Początkowo przyjąłem wydruk taki. Jego zalety to zebranie danych o ilości i trybie wymiany dla całego osiedla, ponumerowanie stron (w układzie nr strony bieżącej/ilość wszystkich stron) oraz wprowadzenie daty na wydruku (każdy kto cyklicznie wykonuje podobne wydruki i chociaż raz dochodził do tego, który wydruk był ostatni, albo czy nie poginęły któreś ze stron doceni dwie ostatnie sprawy). Wadą jest natomiast zbyt duża ilość danych. Każdy kto mieszka w dużych blokowiskach na pewno zetknął się z drukiem rozliczenia kosztów centralnego ogrzewania w oparciu o podzielniki kosztów. Często te druki zawierają kilkanaście/kilkadziesiąt liczb podczas gdy potrzebnych do sprawdzenia poprawności rozliczenia jest tylko kilka. Duża ilość danych może i sugeruje dokładność, ale pogarsza czytelność wydruku. Biorąc te ostatnie pod uwagę obecnie przyjąłem wydruk taki. Na jednej stronie symbolem 'X' zaznaczone są wejścia, którymi wykonujący wydruk był zainteresowany. Pod tabelką z oznaczeniami wejść znajdują się podsumowania wg typów okien oraz całościowe. Boki zarezerwowane są dla czegoś w rodzaju legendy - symbole katalogowe okien nie każdemu odbiorcy wydruków muszą być znane. Niezależnie od ilości i numerów wejść wydruk dostępny jest w stałym układzie tabeli. To o czym zapomniałem to data wydruku - czeka na wprowadzenie ;) Jeżeli odbiorca zgłosi takie zapotrzebowanie to można przewidzieć w programie wydruk w postaci graficznej, przy czym trzeba pamiętać, że obrazowość nie idzie tutaj w parze z wartościami ściśle użytkowymi zawartych danych, ale cóż jak mówił oszust w 'Vabanku' - 'nasz klient nasz per pan'. Z tego powodu dla ostatniego podanego wydruku tabelarycznego zamieszczam wydruk graficzny dostępny w omawianym programie. Zakres danych na wydruku dobrze jest dostosować do odbiorcy i na proste pytanie udzielać w miarę prostej odpowiedzi, a nie odpowiedzi w stylu 'mogę z umiarkowaną stanowczością potwierdzić, że to stwierdzenie jest lub nie jest prawdziwe'. Postaram się wyjaśnić to na następującym przykładzie - zasoby mieszkaniowe jednostki administracyjnej, w której pracuję podzielone są na siedem nieruchomości z rozdzielnymi funduszami remontowymi. Wymiana okien realizowana jest w obrębie nieruchomości. Jeżeli mieszkaniec zadaje pytanie ile okien jest wymienionych to odpowiedź zawarta jest na wydruku takiego typu. Jeśli natomiast jest zainteresowany danymi finansowymi lub przełożeni wymagają tego typu danych to odpowiedź zawarta jest na wydruku takiego typu. Udzielanie na każde pytanie obszernej i długiej odpowiedzi powoduje, że temat jest niejasny.

Dążąc do podsumowania i uogólnienia powyższych wywodów, możliwości omawianych typów programów w zakresie czytelności wydruków muszą obejmować:

  • Wydruk w postaci tabelarycznej tylko niezbędnych dla odbiorcy danych,
  • Na wydruku podana jest data wykonania,
  • Na wydruku podana jest ilość i numeracja stron,
  • Dostępny jest wydruk w formie graficznej,
  • Stopniowanie wydruków co do szczegółowości zawartych danych,
  • Wydruki w miarę możliwości powinny być ujednolicone w formie,

Dla osób zainteresowanych tematem wydruków VBA polecam artykuł z działu 'Zagadnienia VBA':
Wydruk zakresu arkusza z poziomu VBA .
Excel ma również możliwość druku dokumentu Worda (tworzy zmienną obiektową). Excel może, ale nie musi informować użytkownika aplikacji, że zarządza w danej procedurze Wordem - osobom zainteresowanym takim rozwiązaniem polecam:
Wydruk dokumentu Worda z poziomu Excela

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Program VBA bezpieczny w pracy KLIKNIJ by (roz)winąć

W chwili gdy prowadzenie ewidencji planujemy wykonać w postaci elektronicznej pojawia się obawa przed ewentualną awarią. Każdy użytkownik komputera na pewno nie raz i nie dwa stracił dane. Bezpieczeństwo omawianych programów zapewniane jest w nastepujący sposób:
Dyski wszystkich użytkowanych w firmie komputerów dzielą się na dwa dyski logiczne C i D. Dla wygody programy w excelu w postaci pojedynczych plików pracują z poziomu pulpitu, czyli dysku C. Wszystkie kopie zapasowe programów tworzone są domyślnie na dysku D. Mała dygresja: program Auto-Cad tworzy plik zapasowy rysunku w postaci pliku z rozszerzeniem .bak (plik roboczy ma rozszerzenie .dwg). Jeżeli plik roboczy ulegnie uszkodzeniu w pliku zapasowym zmieniane jest rozszerzenie i stanowi on pełnowartościowy plik roboczy. W swoich programach przyjąłem zasadę tworzenia wielu plików zapasowych - każdy z nich ma nazwę uzupełnioną o datę i godzinę utworzenia, tak więc w zasadzie wszystkie zmiany wstecz mogę odtworzyć dzień po dniu. Przy obecnych wielkościach dysków kopie zapasowe plików w rozmiarach od 5 do 40 MB są do zaakceptowania. Wszystkie pliki zapasowe są raz w miesiącu zgrywane na płytę, a codziennie na pamięć przenośną. W zależności od programu tworzenie kopii zapasowej wymaga albo polecenia użytkownika albo związane jest ze zdarzeniem 'Workbook_BeforeClose' dla programu. W niektórych przypadkach regularnie wykonywane są wydruki zestawień z programów, ale to w zasadzie nie jest konieczne.
Obsługa błedu VBA dla wprowadzania danych Innym zagadnieniem jest konieczność sprawdzania poprawności wprowadzanych do programu danych. Nie należy zakładać prób sabotażu, a raczej zwyczajne ludzkie pomyłki. Dane wprowadzane są najczęściej w oknach tekstowych - ich zawartość przed zapisem jest sprawdzana co do typu danych (liczba czy tekst) lub co do zakresu danych np. program OKNA EWIDENCJA dopuszcza kilka różnych typów działań związanych z wymianą stolarki oznaczonych odpowiednimi symbolami (k, e, ebk, ePK, wPK, Z, w, BK). Okna tekstowe do wprowadzania danych dopuszczają wprowadzenie tylko takich wartości, w przypadku wystąpienia błędu wyświetlany jest odpowiedni komunikat. Na ilustracji widać próbę wprowadzenia zapisu 'ePK '(ze spacją) - program nie zapisał danych i w oknie komunikatu (MsgBox) poinformował użytkownika o typie i miejscu popełnionego błędu.
Chyba najtrudniejszym do przewidzenia źródłem błędów w programie jest jego użytkownik ;) Jeden z wykładowców PP uczył studentów 'jeżeli urządzenie ma tylko dwa przyciski to konieczne jest sprawdzenie, czy wciśnięcie obydwu jednocześnie nie zepsuje urządzenia' i coś w tym jest. Jednocześnie trochę jak na ironię komunikat o błędzie z propozycją debugowania jest dla użytkownika bardzo deprymujący. Ponieważ autor programu nie jest w stanie przewidzieć wszystkich zachowań użytkowników (próbowałem, ale życie nauczyło mnie pokory ;) dobrze jest każdą procedurę 'ubrać' w ramki obsługi błędu: 'On Error GoTo Obsługa_błędu:' i po 'Exit Sub' dać np. polecenie zamknięcia wszystkich formularzy i wyjścia do menu głównego - generalna zasada informatyki 'wyjdź i wejdź jeszcze raz' jest jakoś tak łatwiej akceptowalna niż prawidłowa obsługa programów ;)
Hasło administratora Kolejnym zagadnieniem jest konieczność uniknięcia klasycznej sytuacji, w której wszyscy używali programu, ale winnego wprowadzenia błędnych danych nie ma - uzupełnianie danych powinno być przypisane konkretnemu pracownikowi, który tę funkcję zabezpiecza na indywidualnie definiowane hasło. Przed uruchomieniem formularza wyświetlane jest okno z polem tekstowym i żądaniem wprowadzenia hasła (dla TextBox zdefiniowana jest właściwość PasswordChar - domyślnie pusta). Tutaj ważna uwaga - ze względu na udostępnianie pliku do pracy w sieci lepiej jest ograniczać hasłem dostęp do formularzy niż zabezpieczać na to samo hasło arkusze robocze - dla pliku udostępnionego polecenia VBA 'Arkusz1.Protect ([Haslo_uzytkownika])' oraz 'Arkusz1.Unprotect ([Haslo_uzytkownika])' są niedostępne i powodują powstawanie błędu. Dużo lepiej ograniczyć dostęp do formularzy, a arkusze ukryć wykorzystując właściwość 'Visible'. Inny problem pojawia się, gdy pracownik odpowiedzialny za wprowadzanie danych nie pojawi się w pracy. Warto więc przewidzieć w programie np. hasło administratora uprawnionego do odczytu hasła użytkownika (patrz ilustracja) - znowu formularz (dla programu REJESTR WODOMIERZY dostępny z grafiki w centrum ekranu).

Dążąc do podsumowania i uogólnienia powyższych wywodów, program bezpieczny w pracy to taki, który:

  • Tworzy własną kopię bezpieczeństwa automatycznie lub na polecenie użytkownika,
  • Wszystkie jego procedury zawierają obsługę błędów,
  • Sprawdza poprawność wprowadzanych w TextBox-ach danych,
  • Zmiana i wprowadzanie danych dostępne jest na hasło,
  • Uwzględniona jest funkcja administratora programu z uprawnieniami nadrzędnymi w stosunku do użytkownika,

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Przyjazna instrukcja obsługi programu VBA

Do każdego z przedstawianych programów powstały instrukcje obsługi. Pisząc je wzorowałem się na układzie oryginalnych instrukcji pakietu programów do obliczeń cieplnych budynków OZC, GREDI itp. (związane z moim kierunkiem kształcenia). Instrukcje omawiały funkcja po funkcji działanie programu tekst wzbogacając o liczne zrzuty ekranu obrazujące poszczególne etapy pracy. Powstała instrukcja była może długa, ale czytelna i uporządkowana dla każdego kto poświęcił czas na zapoznanie się z nią. Rozwiązanie to sprawdza się tylko w przypadku części użytkowników programu. Biorąc pod uwagę doświadczenia z wprowadzania programów do użytku obecnie przyjąłem zasadę, że oprócz tego typu instrukcji obsługi tzw. pełnej piszę również wyciąg z instrukcji obsługi, którego układ jest prosty - zebrane w spisie treści zapytania użytkowników i wewnątrz instrukcji odpowiedzi w czytelnym układzie kolejnych kroków ze zrzutami ekranu obrazującymi poszczególne etapy. Powoduje to, że wiele informacji się powtarza, ale decydująca jest wygoda użytkownika. Dla zobrazowania przyjętej zasady proponuję zapoznać się ze spisem treści pełnej instrukcji oraz spisem treści wyciągu z tej instrukcji dla programu VBA BAZA TELEADRESOWA. Innym przykładem jest program VBA WYKUP GRUNTÓW i jego instrukcja obsługi oraz wyciąg z tej instrukcji.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Od czego zacząć programowanie w VBA KLIKNIJ by (roz)winąć

Programowanie rozumiane jako pisanie konkretnych ciągów instrukcji w edytorze VBA powinno być po prostu kolejnym etapem prac. Ten pierwszy odbywa się jeszcze przed włączeniem komputera. Najpierw należy dokładnie określić jakim celom program ma służyć i jakie są potrzeby jego docelowych użytkowników. Wiąże się to, przynajmniej w moim wypadku, z wielodniowym notowaniem wszystkich mniej lub bardziej udanych pomysłów i przemyśleń. Jednocześnie w pewnym sensie ten etap jest najważniejszy - jeżeli nie będzie rzetelnie wykonany powstanie program chaotyczny i niepełny. Należy też pamiętać, że o ile wyposażenie programu w nowe funkcje jest stosunkowo proste i często nie musi powodować konfliktów, to zmiana już istniejących funkcji i poprawianie błędów w przyjętych założeniach jest dużo trudniejsze i nie zawsze da się wykonać bezkolizyjnie. Na tym etapie autor programu powinien jednocześnie określać zakres, ułożenie i wzajemne powiązania tabel w poszczególnych arkuszach roboczych z których program będzie korzystał oraz zakres i stopniowanie danych podawanych na wydrukach. Oczywiście im więcej doświadczenia w pisaniu programów tym lepiej dobrane będą wstępne tabele i ich wzajemne powiązania, a także układ wydruków. Dotyczy to zarówno drobiazgów (np. edytor VBA może stwarzać dość nieprzyjemne problemy przy odwoływaniu się do komórek zespolonych tzn. w sytuacji pobierania lub zapisywania w nich danych oraz przy ich formatowaniu) jak i spraw poważniejszych (np. wzajemne odwoływanie się wartości komórek w kilku arkuszach lub praca na ciągach tekstowych bardzo zwiększają rozmiar pliku wynikowego - niektóre funkcje lepiej jest realizować w tabelach MS Excel inne bezpośrednio z poziomu edytora VBA). Etap projektowania programu obejmuje również przypisanie poszczególnych jego funkcji do przycisków menu, zaplanowanie układu formularzy, decyzję o możliwości pracy w sieci itd. itp. Nie jest to może wyznacznik, ale notatki obejmujące założenia do stosunkowo prostego programu WINDYKACJA KOREPONDENCJA zajęły około czterdziestu stron formatu A-4.
Następny etap prac odbywa się już przy włączonym komputerze ;) Obejmuje on wprowadzenie we wszystkich przewidzianych arkuszach roboczych tabel na których program będzie pracował oraz ich funkcji (warto szczegółowo zapoznać się ze standardowymi funkcjami MS Excel - często przydatne są funkcje logiczne czy tekstowe). Ponieważ podczas pisania programu będziemy do naszych tabel wiele razy zaglądać warto je odpowiednio sformatować - program pobierze i zapisze dane w komórce o każdej szerokości, ale warto jej wymiary, czasem również kolor i obramowanie dobrać tak by później nie zwiększać własnych twórczych uciążliwości.
Kolejnym etapem prac jest wykreślenie menu oraz poszczególnych formularzy uruchamianych z przycisków. Warto od razu zmieniać nazwy poszczególnych formantów (właściwość 'Name' - nie mylić z 'Caption') - przy małych formularzach ComboBox1 czy CommandButton4 może i są czytelne, ale przy większych lepiej jest operować ComboBox_Miesiące czy CommandButton_Zapisz. Osobiście nie zmieniam nazw formularzy (UserForm) oraz pozostawiam nazwę formantu wymieniając tylko numer na opis np. zamiast TextBox5 stosuję TextBox_Adres, ale to oczywiście zależy od indywidualnych upodobań. Dla wygody warto jest wykonać wydruk każdego formularza (przez zrzut ekranu) i na wydruku opisać identyfikatory poszczególnych formantów. Wydruk ten potem używam w czasie pracy nad programem.
Następnym etapem jest zaprogramowanie wszystkich zdarzeń dla formularzy - kolejno inicjalizacja, aktywowanie, zamknięcie formularza (UserForm_Initialize, UserForm_Activate, UserForm_QueryClose) itd. Później zdarzenia dla poszczególnych formantów (które się nawzajem uruchmiają, blokują, na który przenoszą fokus, jaka jest kolejność tabulatora itp.) - dopiero po przetestowaniu tego etapu można w końcu ruszyć do meritum sprawy i zacząć pisać instrukcje. Tutaj mała uwaga, na którą sam wpadłem przy programie VBA BAZA TELEADRESOWA. Z punktu widzenia VBA plik Excela może składać się z arkuszy (Microsoft Excel Objects), formularzy (Forms), modułów (Modules) i modułów klasy (Class Modules). Siłą rzeczy program, na którym pracujemy za pośrednictwem formularzy jest zorientowany zdarzeniowo, a najważniejsze jego funkcje wprowadzone są w formie procedur w formularzach. Duża część pracy związanej z programowaniem polega niestety na szukaniu błędów - klasyczne 'co źle zrobiłem, że to pudło nie chce mnie słuchać' ;). Im dłuższe procedury w formularzach tym mniejsza higiena pracy. Warto jest korzystać z wywoływania procedur zewnętrznych z poziomu procedury w formularzu. Polecenie 'Call Nazwa_procedury' opatrzone komentarzem w którym module można podprocedurę znaleźć znacznie ułatwia pracę. Jedyna różnica polega na tym, że odwołując się do formantu danego formularza spoza jego obszaru należy nazwę formantu (Name) poprzedzić nazwą formularza: formant TextBox_Adres staje się tym samym formantem UserForm1.TextBox_Adres. Procedury programu BAZA TELEADRESOWA po przeniesieniu do Worda (kopiuj i wklej) zajmują ponad sześćset stron, na początku niefrasobliwie nie stosowałem polecenia Call, więc efektywność mojej pracy była stosowna do jej organizacji :(
Generalnie pisanie programu najlepsze jest na początku (budowanie założeń i projekt wstępny) i na końcu (wszystko już działa, przekazujemy program do stosowania). Cały środek to kupa żmudnego dłubania.

Dążąc do podsumowania i uogólnienia powyższych wywodów, ramowy tok pracy nad programem obejmuje:

  • Wstępne założenia do programu, określenie oczekiwań i przewidywanych możliwości,
  • Budowa wszystkich tabel w arkuszach roboczych, określenie ich wzajemnych powiązań, formuł oraz sformatowanie,
  • Wykreślenie grafiki głównego menu i narysowanie formularzy,
  • Zmiana nazw wszystkich niezbędnych formantów i wydruk roboczy formularzy,
  • Zaprogramowanie zdarzeń dla całego pliku i dla formularzy,
  • Programowanie zorientowane zdarzeniowo formantów wewnątrz formularzy z właściwym wykorzystaniem podprocedur,
  • Na koniec prac - ustawienie właściwości 'Visible' arkuszy i podziwianie efektu końcowego ;)

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wykonanie kopii bezpieczeństwa w VBA KLIKNIJ by (roz)winąć

Najpierw kilka słów wstępu: przykład wykonania kopii bezpieczeństwa pochodzi z programu OKNA EWIDENCJA, chociaż to akurat nie ma znaczenia dla samego algorytmu. Założenia przedstawione są w punkcie 'Program bezpieczny w pracy?', tak w skrócie: kopią bezpieczeństwa będzie ten sam plik tylko z dodaną datą dzienną i godziną utworzenia zapisywany na innym dysku logicznym niż dysk roboczy. Wiekszość poleceń w VBA zawiera elementy, które można przypisać do zmiennych i uzyskać w ten sposób wiekszą elastyczność (ta sama zasada dotyczy również np. 'Wydruku z poziomu Visual Basica tabeli o zmiennym zakresie wierszy/kolumn'). Cały problem to właściwie dobrać typy zmiennych. Visual Basic dopuszcza deklarację zmiennych w toku wykonywania algorytmu, określenie typu jest również opcjonalne (zmiennej niezdefiniowanego typu przypisywany jest typ Variant) - przynajmniej teoretycznie. W praktyce najlepiej jest zmienne deklarować na początku procedury, podając jednocześnie ich typ, a dodatkowo w generalnych ustawieniach wprowadzić polecenie 'Option Explicit' (jest to wymuszenie deklaracji zmiennej przed jej użyciem - zapobiega to sytuacji gdy wprowadzimy zmienną o omyłkowo innej nazwie np. zmienionej jednej literze w nazwie, którą program potraktuje jako nową zmienną typu Variant). Jest to konieczne by możliwym było usystematyzowanie analizy błędów. W przykładzie określenie 'Arkusz11' dotyczy arkusza pomocniczego, który przez cały czas działania programu jest niewidoczny - właściwość 'Visible = xlSheetHidden'. Wszystkie zmienne dotyczące nazwy zdefiniowane są jako typ 'String'. Zmiennym 'Data_zapisu', 'Godzina', 'Minuta' przypisane są wartości komórek ze standardowymi funkcjami Excela odpowiednimi do nazw zmiennych. Zmiennym 'Ścieżka_pracy' oraz 'Ścieżka_archiwum' przypisywane są łańcuchy tekstowe określone przez użytkownika i wprowadzone do Arkusza11 np. Ścieżka_pracy = 'C:\Documents and Settings\user\Pulpit\'. Działanie algorytmu polega na zapisaniu pliku pod nazwą będącą połączeniem określonych łańcuchów tekstowych na wskazanym w 'Ścieżce_archiwum' miejscu i powtórne zapisanie pliku w miejscu pracy wskazanym odpowiednią zmienną. Algorytm można przypisać do odpowiedniego przycisku, zdarzenia zamknięcia pliku, zmiany aktywnego pliku itp. Część określona przez On Error GoTo Et: to obsługa błędu. Działanie makropolecenia trochę prymitywne, ale skuteczne. Uwagi ogólne:
Podczas nadpisywania pliku MS Excel zapyta czy zastąpić istniejący - odpowiedź negatywna powoduje powstanie konfliktu między makropoleceniem, a działaniem Excela ze skutkiem w postaci denerwującego komunikatu. Można wrzucić to do obsługi błędu, określając, że w przypadku błędu arkusz roboczy zostanie zamknięty lub coś podobnego. Program natrafiając na błąd, w pierwszej kolejności wykonuje polecenia z obsługi błędu, dopiero gdy ich nie znajdzie wyskakuje z komunikatem. W ten sposób użytkownik powodując błąd, nie zobaczy komunikatu o błędzie, tylko uzyska to co chce - taka 'ucieczka do przodu' ;).
Polecenie MsgBox pozwala wyświetlić okno komunikatu, ale bez żadnego zdefiniowania jego wyglądu, ilości przycisków, ikony, nazwy w pasku górnym itp. Definicja wyglądu okna MsgBox jest możliwa tylko po przypisaniu go do zmiennej - stąd zmienna 'Dim i As Integer'. Dlaczego Integer? Chyba z przyzwyczajenia.

UWAGA !
Jeden z czytelników strony wskazał mi nieścisłe informacje o funkcji MsgBox zawarte w tym punkcie. Z uwagi na ten fakt oraz częste poszukiwanie informacji o oknie komunikatu przez osoby wchodzące na stronę, na podstawie uzupełnionej literatury (J. Walkenbach i Green/Bullen/Bovey/Alexander) opracowałem dokładniejszy materiał dotyczący w/w funkcji.
Można go zobaczyć na podstronie 'Zagadnienia VBA' niniejszej witryny.
Jeżeli zauważasz jeszcze jakieś nieścisłości - daj znać.

KOPIA BEZPIECZEŃSTWA Kliknij, żeby (roz)winąć listing

Sub Kopia_bezpieczeństwa()

Dim Ścieżka_pracy As String, Ścieżka_archiwum As String, Data_zapisu As String, Godzina As String, Minuta As String, Zapis_archiwum As String, Zapis_pracy As String
Dim i As Integer

Arkusz1.Select
Range("A1").Select

Data_zapisu = Arkusz11.Range("C21").Value
Godzina = Arkusz11.Range("C19").Value
Minuta = Arkusz11.Range("C20").Value

Ścieżka_pracy = Arkusz11.Range("B12").Value
Ścieżka_archiwum = Arkusz11.Range("B14").Value
Zapis_archiwum = Ścieżka_archiwum & "OKNA_EWIDENCJA_" & Data_zapisu & "__" & Godzina & "__" & Minuta & ".xls"
On Error GoTo Et

ActiveWorkbook.SaveAs Filename:=Zapis_archiwum, FileFormat:=xlNormal, Password:="", WriteResPassword:="", ReadOnlyRecommended:= False, CreateBackup:=False

Zapis_pracy = Ścieżka_pracy & "OKNA_EWIDENCJA" & ".xls"
ActiveWorkbook.SaveAs Filename:=Zapis_pracy, FileFormat:=xlNormal, Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, CreateBackup:=False

Exit Sub

Et: i = MsgBox("NIEPRAWIDŁOWA ŚCIEŻKA DOSTĘPU PRACY LUB ARCHIWUM - WEJDŹ DO MENU I PODAJ PRAWIDŁOWĄ", 0, "OKNA EWIDENCJA")

End Sub

Jednym z niezbędnych elementów bezpiecznej w użytkowaniu aplikacji jest możliwośc tworzenia kopii bezpieczeństwa. Jedną z możliwości - może mało finezyjną, ale skuteczną - przedtawiam w tym punkcie. Wyżej opisaną metodę stosuję z niewielkimi modyfikacjami we wszystkich swoich programach. Kilka razy przekonałem się o jej skuteczności. W załączeniu punktu przedstawiam listing makropolecenia realizującego podane w tytule punktu zadanie.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wydruk z poziomu Visual Basica tabeli o zmiennym zakresie wierszy/kolumn KLIKNIJ by (roz)winąć

Wydruk z poziomu VBA fragmentu arkusza o stałym obszarze to drobiazg. Poniżej w przykładzie 1 przedstawiam przykład pochodzący z programu OKNA EWIDENCJA - wydruk tzw. wizytówki lokalu. Po użyciu przycisku menu głównego 'Szukaj danych mieszkania' następuje wyświetlenie wyszukanych wartości w formularzu i wstawienie ich do odpowiednich komórek arkusza o nazwie 'Wizytówka'. Kliknięcie na przycisku 'Drukuj lokal' tego formularza tj. CommandButton2 powoduje wykonanie makropolecenia przypisanego do zdarzenia 'Click'.
Zwracam uwagę na kilka spraw:
Po pierwsze: typy zmiennych 'Zakres_druku' oraz 'Obszar_druku' - inne typy spowodują komunikat o błędzie,
Po drugie: wydruk następuje z innego arkusza (Arkusz2) niż aktywny (Arkusz1). Nie ma możliwości ustawienia wydruku na arkuszu, który jest niewidoczny - stąd makropolecenie zamknięte w ramy 'Arkusz2.Visible = xlSheetVisible' i na końcu ''Arkusz2.Visible = xlSheetHidden',
Po trzecie: obszar wydruku określamy jako R (wiersze) i C (kolumny) lewego górnego rogu i prawego dolnego,
Po czwarte: W poleceniu drukuj musi wystąpić 'Worksheets("Wizytówka")' - nie można odwołać się do 'Arkusza2'.
Teraz przykład 2 z programu BAZA TELEADRESOWA. Wydruk dotyczy zestawienia wszystkich lokali, które podały adres korespondencyjny - po wyszukaniu program wstawia je do tabeli o zdefiniowanym wyglądzie i początku, a przed wydrukiem określa gdzie tabelka się kończy.
Jako komórkę kontrolną ustawiłem komórkę na końcu dopuszczalnego zakresu (tzn. gdyby wszystkie lokale osiedla miały zdefiniowane adresy korespondencyjne). Pętla 'Do Loop Until' sprawdza kolejne komórki w kierunku z dołu do góry powtarzając pętlę, aż do napotkania pierwszej zajętej. Przy każdym powtórzeniu pętli zostanie zmniejszona wartość zmiennej s, która za chwilę zostanie włączona do łańcucha tekstowego dla zmiennej 'Obszar_druku'. Niby niewielka filozofia, ale bardzo długo szukałem pod jakimi typami zmiennych mają kryć się poszczególne wartości. Kod po pewnych modyfikacjach można stosować do szukania ilości zajętych kolumn i dostosowania zakresu drukowanego. Jednak z pewnych ważnych względów polecam zapoznać się z tematem: 'Ustawienia wstępne wydruku, a szybkość działania programu' oraz 'Wydruk tabel z formułami - sumowaniem/kopiowaniem i usuwaniem wierszy'.
Dla osób zainteresowanych tematem wydruków VBA polecam artykuł z działu 'Zagadnienia VBA':
Wydruk zakresu arkusza z poziomu VBA oraz Wydruk dokumentu Worda z poziomu Excela.

WYDRUK - PRZYKŁAD I - Kliknij, żeby (roz)winąć listing

Private Sub CommandButton2_Click()

'DRUKOWANIE WIZYTÓWKI MIESZKANIA
Dim Zakres_druku As Areas
Dim Obszar_druku As String

'ODBLOKOWANIE ARKUSZA
Arkusz2.Visible = xlSheetVisible

'USTAWIANIE OBSZARU WYDRUKU
Obszar_druku = "='Wizytówka'!R3C2:R39C10"

Worksheets("Wizytówka").Names.Add Name:="Zakres_druku", RefersToR1C1:= Obszar_druku

'DRUKOWANIE
Worksheets("Wizytówka").PageSetup.PrintArea = "Zakres_druku"

Worksheets("Wizytówka").PrintOut Copies:=1, Collate:=True

Arkusz2.Visible = xlSheetHidden
End Sub

WYDRUK - PRZYKŁAD II - Kliknij, żeby (roz)winąć listing

Private Sub CommandButton3_Click()

'DRUKOWANIE
Dim Kontrola As Range
Dim Zakres_druku As Areas
Dim ss As String
Dim s As Single
Dim Obszar_druku As String

'Zaznaczanie obszaru wydruku
Set Kontrola = Arkusz23.Range("B2792")
ss = ""
s = 2792

Do
ss = Kontrola.Value
If ss = "" Then Set Kontrola = Kontrola.Offset(-1, 0)
If ss = "" Then s = s - 1
Loop Until ss <> ""

Arkusz23.Visible = xlSheetVisible
Obszar_druku = "='Adresy_korespondencyjne'!R2C2:R" & s & "C7"

Worksheets("Adresy_korespondencyjne").Names.Add Name:="Zakres_druku", RefersToR1C1:= Obszar_druku

'DRUKOWANIE
Worksheets("Adresy_korespondencyjne").PageSetup.PrintArea = "Zakres_druku"

Worksheets("Adresy_korespondencyjne").PrintOut Copies:=1, Collate:=True

Range("A1").Select

Arkusz23.Visible = xlSheetHidden
End Sub

Wydruk tabel w różnych konfiguaracjach jest jednym z pierwszych zadań, których wykonania oczekujemy od aplikacji. Makra rejestrowane pozwalają prześledzić proces wydruku danych z aktualnego arkusza dla stałych zakresów. Następnym etapem jest wydruk z arkusza innego niż aktywny oraz wydruk zmiennego zakresu tabeli. Przykłady rzeczywistych realizacji tych zadań przedstawione są w tym punkcie.
Dla osób zainteresowanych tematem wydruków VBA polecam artykuł z działu 'Zagadnienia VBA':
Wydruk zakresu arkusza z poziomu VBA oraz Wydruk dokumentu Worda z poziomu Excela.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wymiana paska menu MS Excel KLIKNIJ by (roz)winąć

Celem jest uzyskanie własnego paska menu - omawiany przykład pochodzi z pliku FAKTURA WEWNĘTRZNA. Całe menu MS Excel zostało zastąpione menu własnym, a napis na pasku górnym napisem własnym. Oczywiście można w ten sposób wyłączyć wszystkie paski narzędzi, włączyć opcję pełnego ekranu itp. Spowoduje to, że nasza aplikacja swoją podstawę jaką jest MS Excel będzie zdradzała tylko charakterystyczną ikonką w lewym górnym rogu ekranu.Własny pasek menuTakie własne menu może być uruchamiane wraz z włączeniem pliku (zdarzenie dla 'WorkbookOpen'). W przypadku mojego pliku wywoływane jest kliknięciem w odpowiedni klawisz i podaniem hasła - stąd wartość sprawdzanej komórki 'TAK'. Cała reszta to zdarzenia dla 'ThisWorkbook' wg poniższego zestawienia.
Uwaga: Ta metoda działa tylko w wersjach MS Excel poniżej 2007. W tym ostatnim dla wszystkiego co użytkownik programowo wprowadza tworzona jest zakładka 'Dodatki'. Całość działa poprawnie, ale nie ma już tego efektu - wstążka szybkiego dostępu jest jak na razie nie do ruszenia (przynajmniej dla mnie).
Mała uwaga:
Umiejscowienie zarówno procedury zmiany menu jak i makra z paramentu 'OnAction' w zakładce 'ThisWorkbook' wynika z niewłaściwego zinterpretowania przez mnie zapisów w książce pani Snarskiej. Po uzupełnieniu biblioteczki (Walkenbach) jak i wiedzy wiem już że tak nie musi być - artykuł z bardzo czytelnym przykładem zawarłem w zakładce 'Zagadnienia VBA' Modyfikacja standardowego menu Excela.
Dla przeanalizowania sposobu działania powyższych procedur można zapoznać się z instrukcją programu. Sam przepis na zastępowanie menu Excela zaczerpnięty jest z książki pani Agnieszki Snarskiej.

ZMIANA PASKA MENU Kliknij, żeby (roz)winąć listing

Private Sub Workbook_Activate()
If Arkusz1.Range("C1").Value = "TAK" Then
'NOWE MENU
On Error GoTo Et 'Obsługa błędu

MenuBars.Add ("Faktura miesięczna konserwatorów") 'Pasek menu nadrzędnego

With MenuBars("Faktura miesięczna konserwatorów") 'Paski menu głównego
.Menus.Add ("Aplikacje własne")
.Menus.Add ("Eksport danych")
.Menus.Add ("Dekretacja robót")
.Menus.Add ("Koniec")
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Aplikacje własne")
.MenuItems.Add Caption:="Otwórz OKNA_EWIDENCJA", OnAction:="ThisWorkbook.OKNA_EWIDENCJA"
StatusBar = "Wiersz stanu - otwórz OKNA EWIDENCJA"
.MenuItems.Add Caption:="Otwórz REJESTR WODOMIERZY", OnAction:="ThisWorkbook.REJESTR_WODOMIERZY"
StatusBar = "Wiersz stanu - otwórz REJESTR WODOMIERZY"
.MenuItems.Add Caption:="Otwórz BAZA TELEADRESY", OnAction:="ThisWorkbook.BAZA_TELEADRESY"
StatusBar = "Wiersz stanu - otwórz BAZA TELEADRESY"
.MenuItems.Add Caption:="Otwórz REMONTY WYKONANIE", OnAction:="ThisWorkbook.REMONTY_WYKONANIE"
StatusBar = "Wiersz stanu - otwórz REMONTY WYKONANIE"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Eksport danych")
.MenuItems.Add Caption:="Eksportuj dane DT", OnAction:="ThisWorkbook.EKSPORT_DT"
StatusBar = "Wiersz stanu - eksport dane DT"
.MenuItems.Add Caption:="Eksportuj dane KS", OnAction:="ThisWorkbook.EKSPORT_KS"
StatusBar = "Wiersz stanu - eksport dane KS"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Dekretacja robót")
.MenuItems.Add Caption:="Zmiana kwalifikacji robót", OnAction:="ThisWorkbook.ZMIANA_KWALIFIKACJA"
StatusBar = "Wiersz stanu - Zmiana kwalifikacji robót"
.MenuItems.Add Caption:="Zmiana kont robót", OnAction:="ThisWorkbook.ZMIANA_KONT"
StatusBar = "Wiersz stanu - Zmiana dekratacji kont"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Koniec")
.MenuItems.Add Caption:="Przywróć menu Excela", OnAction:="ThisWorkbook.PRZYWRÓĆ_MENU_EXCEL"
StatusBar = "Wiersz stanu - Przywróć menu Excela"
.MenuItems.Add Caption:="Zmiana haseł", OnAction:="ThisWorkbook.ZMIANA_HASEŁ"
StatusBar = "Wiersz stanu - Zmiana haseł"
.MenuItems.Add Caption:="Informacja o programie", OnAction:="ThisWorkbook.INFO"
StatusBar = "Wiersz stanu - Informacja o programie"
.MenuItems.Add Caption:="Zakończ pracę", OnAction:="ThisWorkbook.KONIEC_PRACY"
StatusBar = "Wiersz stanu - Zakończenie pracy"
End With

MenuBars("Faktura miesięczna konserwatorów").Activate
Application.StatusBar = True
Application.Caption = "FAKTURA MIESIĘCZNA KONSERWATORÓW - Aplikacja własna G.Koralewski. PLIK "
End If

Exit Sub
Et: MsgBox ("Menu własne zostało usunięte")
End Sub

Private Sub Workbook_BeforeClose(Cancel As Boolean)
'KASOWANIE NOWEGO MENU
MenuBars(xlWorksheet).Activate
MenuBars(xlModule).Activate

On Error GoTo Et 'Obsługa błędu

With MenuBars("Faktura miesięczna konserwatorów").Menus("Aplikacje własne")
.MenuItems("Otwórz OKNA_EWIDENCJA").Delete
.MenuItems("Otwórz REJESTR WODOMIERZY").Delete
.MenuItems("Otwórz BAZA TELEADRESY").Delete
.MenuItems("Otwórz REMONTY WYKONANIE").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Eksport danych")
.MenuItems("Eksportuj dane DT").Delete
.MenuItems("Eksportuj dane KS").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Dekretacja robót")
.MenuItems("Zmiana kwalifikacji robót").Delete
.MenuItems("Zmiana kont robót").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Koniec")
.MenuItems("Przywróć menu Excela").Delete
.MenuItems("Zmiana haseł").Delete
.MenuItems("Informacja o programie").Delete
.MenuItems("Zakończ pracę").Delete
End With

MenuBars("Faktura miesięczna konserwatorów").Delete
Application.Caption = "Microsoft Excel"

Arkusz1.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz15.Visible = xlSheetHidden
Arkusz20.Visible = xlSheetHidden
Arkusz22.Visible = xlSheetHidden
Arkusz23.Visible = xlSheetHidden
Arkusz28.Visible = xlSheetHidden
Arkusz31.Visible = xlSheetHidden
Arkusz33.Visible = xlSheetHidden
Arkusz34.Visible = xlSheetHidden
Arkusz35.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetVisible
Arkusz6.Visible = xlSheetVisible
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Exit Sub
Et: ActiveWorkbook.Close
End Sub

Private Sub Workbook_Deactivate()
If Arkusz1.Range("C1").Value = "TAK" Then
'KASOWANIE NOWEGO MENU
MenuBars(xlWorksheet).Activate
MenuBars(xlModule).Activate

On Error GoTo Et 'Obsługa błędu
With MenuBars("Faktura miesięczna konserwatorów").Menus("Aplikacje własne")
.MenuItems("Otwórz OKNA_EWIDENCJA").Delete
.MenuItems("Otwórz REJESTR WODOMIERZY").Delete
.MenuItems("Otwórz BAZA TELEADRESY").Delete
.MenuItems("Otwórz REMONTY WYKONANIE").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Eksport danych")
.MenuItems("Eksportuj dane DT").Delete
.MenuItems("Eksportuj dane KS").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Dekretacja robót")
.MenuItems("Zmiana kwalifikacji robót").Delete
.MenuItems("Zmiana kont robót").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Koniec")
.MenuItems("Przywróć menu Excela").Delete
.MenuItems("Zmiana haseł").Delete
.MenuItems("Informacja o programie").Delete
.MenuItems("Zakończ pracę").Delete
End With

MenuBars("Faktura miesięczna konserwatorów").Delete
Application.Caption = "Microsoft Excel"
End If

Exit Sub
Et: MsgBox ("Menu własne zostało usunięte")
End Sub
Private Sub Workbook_Open()
Arkusz2.Select
Range("A1").Select

Arkusz1.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz15.Visible = xlSheetHidden
Arkusz20.Visible = xlSheetHidden
Arkusz22.Visible = xlSheetHidden
Arkusz23.Visible = xlSheetHidden
Arkusz28.Visible = xlSheetHidden
Arkusz31.Visible = xlSheetHidden
Arkusz33.Visible = xlSheetHidden
Arkusz34.Visible = xlSheetHidden
Arkusz35.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetVisible
Arkusz6.Visible = xlSheetVisible
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Arkusz28.Range("C9").Value = ""
Arkusz28.Range("F9").Value = ""
Arkusz28.Range("F15").Value = ""
Arkusz23.Range("H1").Value = ""
End Sub

Sub OKNA_EWIDENCJA()
Call Otwórz_okna_ewidencja
End Sub

Sub REJESTR_WODOMIERZY()
Call Otwórz_wodomierz
End Sub

Sub BAZA_TELEADRESY()
Call Otwórz_teleadresy
End Sub

Sub REMONTY_WYKONANIE()
Call Otwórz_remonty_wykonanie
End Sub

Sub ZMIANA_KWALIFIKACJA()
UserForm5.Show
End Sub

Sub ZMIANA_KONT()
UserForm8.Show
End Sub

Sub KONIEC_PRACY()
Call PRZYWRÓĆ_MENU_EXCEL
On Error GoTo Et:
ActiveWorkbook.Save
ActiveWorkbook.Close
Exit Sub
Et: ActiveWorkbook.Close

End Sub
Sub ZMIANA_HASEŁ()
UserForm12.Show
End Sub

Sub EKSPORT_DT()
Call Eksport_DT_w
End Sub

Sub EKSPORT_KS()
Call Eksport_KS_w
End Sub

Sub INFO()
UserForm13.Show
End Sub

Sub PRZYWRÓĆ_MENU_EXCEL()
'KASOWANIE NOWEGO MENU
MenuBars(xlWorksheet).Activate
MenuBars(xlModule).Activate

On Error GoTo Et 'Obsługa błędu
With MenuBars("Faktura miesięczna konserwatorów").Menus("Aplikacje własne")
.MenuItems("Otwórz OKNA_EWIDENCJA").Delete
.MenuItems("Otwórz REJESTR WODOMIERZY").Delete
.MenuItems("Otwórz BAZA TELEADRESY").Delete
.MenuItems("Otwórz REMONTY WYKONANIE").Delete
End With
With MenuBars("Faktura miesięczna konserwatorów").Menus("Eksport danych")
.MenuItems("Eksportuj dane DT").Delete
.MenuItems("Eksportuj dane KS").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Dekretacja robót")
.MenuItems("Zmiana kwalifikacji robót").Delete
.MenuItems("Zmiana kont robót").Delete
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Koniec")
.MenuItems("Przywróć menu Excela").Delete
.MenuItems("Zmiana haseł").Delete
.MenuItems("Informacja o programie").Delete
.MenuItems("Zakończ pracę").Delete
End With

MenuBars("Faktura miesięczna konserwatorów").Delete
Application.Caption = "Microsoft Excel"

Arkusz1.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz15.Visible = xlSheetHidden
Arkusz20.Visible = xlSheetHidden
Arkusz22.Visible = xlSheetHidden
Arkusz23.Visible = xlSheetHidden
Arkusz28.Visible = xlSheetHidden
Arkusz31.Visible = xlSheetHidden
Arkusz33.Visible = xlSheetHidden
Arkusz34.Visible = xlSheetHidden
Arkusz35.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetVisible
Arkusz6.Visible = xlSheetVisible
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Exit Sub
Et: MsgBox ("Stosowane menu własne")

End Sub

Sub PRZYWRÓĆ_MENU_WŁASNE()
'NOWE MENU
On Error GoTo Et 'Obsługa błędu

MenuBars.Add ("Faktura miesięczna konserwatorów") 'Pasek menu nadrzędnego

With MenuBars("Faktura miesięczna konserwatorów") 'Paski menu głównego
.Menus.Add ("Aplikacje własne")
.Menus.Add ("Eksport danych")
.Menus.Add ("Dekretacja robót")
.Menus.Add ("Koniec")
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Aplikacje własne")
.MenuItems.Add Caption:="Otwórz OKNA_EWIDENCJA", OnAction:="ThisWorkbook.OKNA_EWIDENCJA"
StatusBar = "Wiersz stanu - otwórz OKNA EWIDENCJA"
.MenuItems.Add Caption:="Otwórz REJESTR WODOMIERZY", OnAction:="ThisWorkbook.REJESTR_WODOMIERZY"
StatusBar = "Wiersz stanu - otwórz REJESTR WODOMIERZY"
.MenuItems.Add Caption:="Otwórz BAZA TELEADRESY", OnAction:="ThisWorkbook.BAZA_TELEADRESY"
StatusBar = "Wiersz stanu - otwórz BAZA TELEADRESY"
.MenuItems.Add Caption:="Otwórz REMONTY WYKONANIE", OnAction:="ThisWorkbook.REMONTY_WYKONANIE"
StatusBar = "Wiersz stanu - otwórz REMONTY WYKONANIE"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Eksport danych")
.MenuItems.Add Caption:="Eksportuj dane DT", OnAction:="ThisWorkbook.EKSPORT_DT"
StatusBar = "Wiersz stanu - eksport dane DT"
.MenuItems.Add Caption:="Eksportuj dane KS", OnAction:="ThisWorkbook.EKSPORT_KS"
StatusBar = "Wiersz stanu - eksport dane KS"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Dekretacja robót")
.MenuItems.Add Caption:="Zmiana kwalifikacji robót", OnAction:="ThisWorkbook.ZMIANA_KWALIFIKACJA"
StatusBar = "Wiersz stanu - Zmiana kwalifikacji robót"
.MenuItems.Add Caption:="Zmiana kont robót", OnAction:="ThisWorkbook.ZMIANA_KONTA"
StatusBar = "Wiersz stanu - Zmiana dekratacji kont"
End With

With MenuBars("Faktura miesięczna konserwatorów").Menus("Koniec")
.MenuItems.Add Caption:="Przywróć menu Excela", OnAction:="ThisWorkbook.PRZYWRÓĆ_MENU_EXCEL"
StatusBar = "Wiersz stanu - Przywróć menu Excela"
.MenuItems.Add Caption:="Informacja o programie", OnAction:="ThisWorkbook.INFO"
StatusBar = "Wiersz stanu - Informacja o programie"
.MenuItems.Add Caption:="Zakończ pracę", OnAction:="ThisWorkbook.KONIEC_PRACY"
StatusBar = "Wiersz stanu - Zakończenie pracy"
End With

MenuBars("Faktura miesięczna konserwatorów").Activate
Application.StatusBar = True
Application.Caption = "FAKTURA MIESIĘCZNA KONSERWATORÓW - Aplikacja własna G.Koralewski"

Exit Sub
Et: MsgBox ("Stosowane menu własne")

End Sub

Jedną z ciekawszych możliwości programowania w Excelu jest zmiana menu głównego Excela. O ile korzystamy z tej możliwości z rozwagą (dbając o przywrócenie standardowego menu przy wychodzeniu z aplikacji), jest ona bardzo przydatna. Najlepsze efekty daje przy Excelu 2003. Dla Excela 2007 wstążka dostępu modyfikowalna jest w dużo mniejszym zakresie. W punkcie prezentowany jest przykład zaczerpnięty z jednego z moich programów. Nie jest doskonały, ale obrazuje możliwości. W zakładce 'Zagadnienia VBA' zawarty jest przykład ogólny dość szczegółowo opisany: Modyfikacja standardowego menu Excela

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Uzupełnienie MS Excel o własne menu KLIKNIJ by (roz)winąć

W temacie powyżej celem było uzyskanie własnego paska menu. Nie zawsze jest to wskazane - czasami chcemy tylko uzupełnić pasek menu MS Excel o własne menu rozwijane. Obecny przykład pochodzi z pliku OKNA SPISY, który stanowi załącznik do programu OKNA WYKONAWSTWO. Menu MS Excel zostało uzupełnione menu własnym (z własnymi ikonami), a napis na pasku górnym napisem własnym. Oczywiście nadal można wyłączyć wszystkie paski narzędzi, włączyć opcję pełnego ekranu itp. Uzupełniony pasek menuTakie własne menu może być uruchamiane wraz z włączeniem pliku (zdarzenie dla 'WorkbookOpen'). W przypadku mojego pliku wywoływane jest zarówno przy pierwszym uruchomieniu (wtedy określamy rok, następuje zmiana nazwy arkusza roboczego oraz zapis pliku pod nową nazwą) oraz przy aktywacji arkusza. Cała reszta to zdarzenia dla 'ThisWorkbook' wg poniższego zestawienia.
Uwaga: Ta metoda działa tylko w wersjach MS Excel poniżej 2007. W tym ostatnim dla wszystkiego co użytkownik programowo wprowadza tworzona jest zakładka 'Dodatki'. Całość działa poprawnie, ale nie ma już tego efektu - wstążka szybkiego dostępu jest jak na razie nie do ruszenia (przynajmniej dla mnie).
Mała uwaga:
Umiejscowienie zarówno procedury zmiany menu jak i makra z paramentu 'OnAction' w zakładce 'ThisWorkbook' wynika z niewłaściwego zinterpretowania przez mnie zapisów w książce pani Snarskiej. Po uzupełnieniu biblioteczki (Walkenbach) jak i wiedzy wiem już że tak nie musi być - artykuł z bardzo czytelnym przykładem zawarłem w zakładce 'Zagadnienia VBA' Modyfikacja standardowego menu Excela.
Polecenie 'FaceId' odnosi się do ikony widocznej po rozwinięciu paska menu.
Dla przeanalizowania sposobu działania powyższych procedur można zapoznać się z instrukcją programu. Sam przepis na uzupełnianie menu Excela zaczerpnięty jest z książki pani Julitty Korol.

UZUPEŁNIENIE MENU MS EXCEL Kliknij, żeby (roz)winąć listing

Private Sub Workbook_Activate()

Dim Aplikacja As Object

If Arkusz13.Range("K1").Value <> "T" Then
Arkusz13.Range("K1").Value = "T" 'znacznik likwidacji menu własnego
Application.Caption = "Aplikacja własna _G.Koralewski"

Application.CommandBars("Worksheet menu bar").Controls.Add(Type:=msoControlPopup).Caption = "Aplikacja własna - OKNA SPISY"
Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=1).Caption = "Informacje o aplikacji"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Informacje o aplikacji")
.FaceId = 124
.OnAction = "Pokaż_info"
End With

Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=2).Caption = "Pokaż/ukryj arkusze robocze"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Pokaż/ukryj arkusze robocze")
.FaceId = 139
.OnAction = "Pokaż_arkusze"
End With

Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Pokaż/ukryj arkusze robocze").BeginGroup = True
Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=3).Caption = "Podaj rocznik"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Podaj rocznik")
.FaceId = 175
.OnAction = "Pokaż_rocznik"
End With
End If

End Sub


Private Sub Workbook_BeforeClose(Cancel As Boolean)

If Arkusz13.Range("K1").Value = "T" Then
Application.Caption = "Microsoft Excel"
Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Delete
End If

Arkusz13.Range("K1").Value = "N"
End Sub


Private Sub Workbook_Deactivate()

If Arkusz13.Range("K1").Value = "T" Then
Application.Caption = "Microsoft Excel"
Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Delete
End If

Arkusz13.Range("K1").Value = "N"
Arkusz2.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz6.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden
Arkusz13.Visible = xlSheetHidden

End Sub


Private Sub Workbook_Open()

Dim Aplikacja As Object
Dim i As Integer

If Arkusz13.Range("K1").Value <> "T" Then
Arkusz13.Range("K1").Value = "T" 'znacznik likwidacji menu własnego
Application.Caption = "Aplikacja własna _G.Koralewski"

Application.CommandBars("Worksheet menu bar").Controls.Add(Type:=msoControlPopup).Caption = "Aplikacja własna - OKNA SPISY"

Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=1).Caption = "Informacje o aplikacji"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Informacje o aplikacji")
.FaceId = 124
.OnAction = "Pokaż_info"
End With

Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=2).Caption = "Pokaż/ukryj arkusze robocze"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Pokaż/ukryj arkusze robocze")
.FaceId = 139
.OnAction = "Pokaż_arkusze"
End With

Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Pokaż/ukryj arkusze robocze").BeginGroup = True
Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls.Add(Type:=msoControlButton, Before:=3).Caption = "Podaj rocznik"
With Application.CommandBars("Worksheet menu bar").Controls("Aplikacja własna - OKNA SPISY").Controls("Podaj rocznik")
.FaceId = 175
.OnAction = "Pokaż_rocznik"
End With
End If
Arkusz2.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz6.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden
Arkusz13.Visible = xlSheetHidden

If Arkusz13.Range("L4").Value = "" Then
i = MsgBox("PIERWSZE URUCHOMIENIE PLIKU", vbInformation + vbOKOnly, "OKNA SPISY")
UserForm6.Show
End If

End Sub


Sub Pokaż_info()
UserForm5.Show
End Sub

Mniej inwazyjną, a równie przydatną co opisana wyżej możliwością programowania w Excelu jest uzupełnienie menu głównego zamiast jego całkowitej zmiany. Jest to metoda mniej kłopotliwa dla użytkownika, a przy wielu aplikacjach całkowicie wystarczająca. Najlepsze efekty daje przy Excelu 2003 chociaż dla Excela 2007 z dużo mniej modyfikowalna wstążką uzupełnienie menu jest zbliżone. W punkcie prezentowany jest przykład zaczerpnięty z jednego z moich programów. Nie jest doskonały, ale obrazuje możliwości. W zakładce 'Zagadnienia VBA' zawarty jest przykład ogólny dość szczegółowo opisany: Modyfikacja standardowego menu Excela

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Ukrywanie arkuszy roboczych programu - obsługa zdarzeń KLIKNIJ by (roz)winąć

Przykład do tego punktu zaczerpnięty jest z pliku VBA REJESTR ZAKUPÓW i mam nadzieję, że będzie dostatecznie czytelnie obrazował możliwości programowania zdarzeń związanych z arkuszami. Sam plik ma pomagać w wykonaniu zestawienia faktur zakupów i omówiony jest na osobnej wyżej wskazanej podstronie. Zdarzenia dla arkuszy w VBAW bieżącym punkcie chciałbym zwrócić uwagę, że sam plik składa się między innymi z dwunastu miesięcznych arkuszy. Ze względu na fakt, że osoba korzystająca z niego niespecjalnie lubi MS Excel taka ilość arkuszy byłaby dla niej niewygodna. Stworzyłem więc jeszcze cztery arkusze odpowiadające kwartałom, przy czym widoczne są tylko trzy - po kliknięciu w arkusz aktywnego kwartału pojawiają się przypisane do niego arkusze miesięczne, a on sam zostaje ukryty. W ten sposób zamiast dwunastu arkuszy widocznych jest sześć - co dla użytkownika jest bardziej czytelne i w dużej mierze zapobiega sytuacjom, w którym wypełniana jest tabela z niewłaściwego miesiąca. Inne zastosowanie omawianej techniki to plik FAKTURA KONSERWATORÓW. Magazyn jest prowadzony w jednym arkuszu MS Excel, w drugim prowadzony jest stan narzędzi i w trzecim środki czystości. Arkusze związane z wykonaniem faktury konserwatorów (kilkanaście) zawarte są w tym samym pliku, ale wykorzystywane raz w miesiącu. Dla osoby obsługującej magazyn taka ilość arkuszy byłaby na co dzień niewygodna (korzysta tylko z trzech - magazyn, narzędzia i środki czystości), więc są ukryte. W chwili, gdy sytuacja dojrzeje do wykonania faktury miesięcznej klikam na odpowiedni przycisk, a program po pozytywnej weryfikacji hasła wykonuje kolejno: ujawnienie arkuszy związanych z fakturą, ukrycie arkuszy środków czystości i narzędzi oraz zapisanie pliku pod nazwą uzupełnioną o datę miesieczną. W ten sposób osoba obsługująca magazyn nadal korzysta ze swojego pliku, a ja mam swój własny plik już opisany do fakturowania.
Wracając do bieżącego przykładu - w listingach porządek arkuszy to:
- Arkusz1 to styczeń,
- Arkusz2 to luty,
- Arkusz3 to marzec,
- ...
- Arkusz12 to grudzień,
- Arkusz13 to kwartał I,
- Arkusz14 to kwartał II,
- Arkusz15 to kwartał III,
- Arkusz16 to kwartał IV,
Wszystkie poniższe procedury wprowadzone są w odpowiednich zdarzeniach dla arkuszy.
Metoda nieskomplikowana, a dająca spore możliwości uczynienia pliku bardziej wygodnym w użytkowaniu.
Mała uwaga - kolejność poleceń tj. ukrycie arkusza bieżącego kwartału na końcu procedury nie jest bez znaczenia. Przy pierwszym podejściu do wykonania opisanego manewru (dodatkowo zaprogramowałem zdarzenia dla arkuszy miesięcznych) komputer zaczął kolejno przełączać arkusze w nieskończoność - przerwałem procedurę i szukałem błędów. Pierwsze polecenia mają na celu aktywowanie arkusza pierwszego miesiąca danego kwartału i ustawienie aktywnych komórek na widoku (ActiveWindow.SmallScroll Down:=-200 oraz Arkusz10.Range("A1").Select).

KWARTAŁ I ARKUSZ 13 Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

'ustawienia na miesiącu
Arkusz1.Activate
ActiveWindow.SmallScroll Down:=-200
Arkusz1.Range("A1").Select

'ujawnienie arkuszy kwartałów/miesięcy
Arkusz14.Visible = xlSheetVisible
Arkusz15.Visible = xlSheetVisible
Arkusz16.Visible = xlSheetVisible

Arkusz1.Visible = xlSheetVisible
Arkusz2.Visible = xlSheetVisible
Arkusz3.Visible = xlSheetVisible

'ukrycie arkuszy kwartałów/miesięcy
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz6.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Arkusz13.Visible = xlSheetHidden

End Sub

KWARTAŁ II ARKUSZ 14 Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

'ustawienia na miesiącu
Arkusz4.Activate
ActiveWindow.SmallScroll Down:=-200
Arkusz4.Range("A1").Select

'ujawnienie arkuszy kwartałów/miesięcy
Arkusz13.Visible = xlSheetVisible
Arkusz15.Visible = xlSheetVisible
Arkusz16.Visible = xlSheetVisible

Arkusz4.Visible = xlSheetVisible
Arkusz5.Visible = xlSheetVisible
Arkusz6.Visible = xlSheetVisible

'ukrycie arkuszy kwartałów/miesięcy
Arkusz1.Visible = xlSheetHidden
Arkusz2.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz9.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Arkusz14.Visible = xlSheetHidden

End Sub

KWARTAŁ III ARKUSZ 15 Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

'ustawienia na miesiącu
Arkusz7.Activate
ActiveWindow.SmallScroll Down:=-200
Arkusz7.Range("A1").Select

'ujawnienie arkuszy kwartałów/miesięcy
Arkusz13.Visible = xlSheetVisible
Arkusz14.Visible = xlSheetVisible
Arkusz16.Visible = xlSheetVisible

Arkusz7.Visible = xlSheetVisible
Arkusz8.Visible = xlSheetVisible
Arkusz9.Visible = xlSheetVisible

'ukrycie arkuszy kwartałów/miesięcy
Arkusz1.Visible = xlSheetHidden
Arkusz2.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz6.Visible = xlSheetHidden
Arkusz10.Visible = xlSheetHidden
Arkusz11.Visible = xlSheetHidden
Arkusz12.Visible = xlSheetHidden

Arkusz15.Visible = xlSheetHidden

End Sub

KWARTAŁ IV ARKUSZ 16 Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

'ustawienia na miesiącu
Arkusz10.Activate
ActiveWindow.SmallScroll Down:=-200
Arkusz10.Range("A1").Select

'ujawnienie arkuszy kwartałów/miesięcy
Arkusz13.Visible = xlSheetVisible
Arkusz14.Visible = xlSheetVisible
Arkusz15.Visible = xlSheetVisible
Arkusz10.Visible = xlSheetVisible
Arkusz11.Visible = xlSheetVisible
Arkusz12.Visible = xlSheetVisible

'ukrycie arkuszy kwartałów/miesięcy
Arkusz1.Visible = xlSheetHidden
Arkusz2.Visible = xlSheetHidden
Arkusz3.Visible = xlSheetHidden
Arkusz4.Visible = xlSheetHidden
Arkusz5.Visible = xlSheetHidden
Arkusz6.Visible = xlSheetHidden
Arkusz7.Visible = xlSheetHidden
Arkusz8.Visible = xlSheetHidden
Arkusz9.Visible = xlSheetHidden

Arkusz16.Visible = xlSheetHidden

End Sub

W punkcie omówiony jest przykład wykorzystania obsługi zdarzeń dla jednej z mniejszych aplikacji. Zdarzenia pozwalają automatyzować pewne zachowania programu tak by jego obsługa był abardziej intuicyjna lub/i wygodniejsza. W prezentowanym przykładzie obsługę zdarzeń wykorzystano by ograniczyć widoczność niektórych spośród arkuszy. Inne zastosowanie (uruchomienie makra wraz z aktywacją komórki) przedstawiłem w dziale "Zagadnienia VBA" w punkcie: Zdarzenie Change arkusza Excela.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Dostosowanie rozdzielczości pracy aplikacji - obsługa zdarzeń VBA KLIKNIJ by (roz)winąć

Większość prezentowanych na stronie programów startuje z arkusza z narysowanym ręcznie menu. Sytuacja, w której menu programu ze względu na rozdzielczość pracy nie mieści się na ekranie lub jest nieproporcjonalnie małe jest trochę żenująca chociaż zdarza się w niektórych produkcjach.Formularz ustawienia rozdzileczości Od razu zaznaczam - działanie, które wykonuje poniższy algorytm polega na doborze odpowiedniej wartości 'zoom' arkusza, ale swoje zadanie spełnia. Przykład zaczerpnięty jest z programu OKNA WYKONAWSTWO, ale akurat ten fragment jest w zasadzie jednakowy we wszystkich moich programach opartych na tym samym menu - układ analogiczny jak w uwadze 'Program intuicyjny w obsłudze?'. Do skutecznej realizacji pomysłu potrzeba arkusza menu + jeden arkusz wyglądający jak arkusz menu (zmiana rozdzielczości następuje w momencie aktywowania arkusza). Całość jest uruchamiana za pośrednictwem przycisku w postaci grafiki w środku ekranu. Kliknięcie na przycisku 'Zmień rozdzielczość' wywołuje formularz jak na rysunku. Użycie któregoś z OptionButton w ramce 'Rozdzielczość' wstawi wcześniej zdefiniowaną wartość do komórki arkusza pomocniczego (która będzie sprawdzana w trakcie aktywowania arkusza po kliknięciu przycisku CommandButton 'Zatwierdź rozdzielczość') oraz odblokuje przycisk CommandButton z zatwierdzeniem rozdzielczości. Dodatkowo krótkie makropolecenia wstawione są w zdarzenia arkuszy menu i arkusza menu-podobnego. Listingi przedstawiam poniżej.
Nowa zmienna 'Haslo_uzytkownika' nie wymaga wyjaśnienia. Efekt graficzny będzie taki, że ekran zamruga przez chwilę i zmieści menu programu na ekranie. Wartości 'ActiveWindow.Zoom' są dobrane doświadczalnie. Oczywiście można rozszerzyć zakres rozdzielczości oraz dobrać również wartości szerokości pierwszych komórek, tak by wyśrodkować menu na ekranie, ale nie zmienia to idei powyższego kodu. Dla zawziętych: można pod kątem rozdzielczości dobrać wielkości wszystkich formularzy - po kliknięciu opcji menu program sprawdzałby jaka jest ustawiona rozdzielczość i w zależności od niej wczytywałby konkretne osobno narysowane formularze wyposażone w dokładnie takie same formanty identycznie oprogramowane.

PRZYCISK OptionButton ramki 'Rozdzielczość' Kliknij, żeby (roz)winąć listing

Private Sub OptionButton1_Click()
CommandButton1.Enabled = True
Arkusz17.Range("E24").Value = "800x600"
End Sub

Private Sub OptionButton2_Click()
CommandButton1.Enabled = True
Arkusz17.Range("E24").Value = "1024x768"
End Sub

Private Sub OptionButton3_Click()
CommandButton1.Enabled = True
Arkusz17.Range("E24").Value = "1280x1024"
End Sub

PRZYCISK CommandButton 'Zatwierdź rozdzielczość' Kliknij, żeby (roz)winąć listing

Private Sub CommandButton1_Click()
Arkusz18.Visible = xlSheetVisible
Arkusz18.Select
Arkusz1.Select
Arkusz18.Visible = xlSheetHidden
End Sub

ZDARZENIA DLA ARKUSZA MENU Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

Dim rozdzielczosc As String
Dim Haslo_uzytkownika As String

Haslo_uzytkownika = Arkusz17.Range("D2").Value

Arkusz1.Unprotect ([Haslo_uzytkownika])
Arkusz18.Unprotect ([Haslo_uzytkownika])

'POBRANIE OBOWIĄZUJĄCYCH WARTOŚCI
rozdzielczosc = Arkusz17.Range("E24").Value

'USTAWIANIE ZOOMU I SZEROKOŚCI KOLUMNY A - MUSI BYĆ WARTOŚCIĄ STAŁĄ PRZY
'SPEŁNIONYM WARUNKU

If rozdzielczosc = "800x600" Then
ActiveWindow.Zoom = ("79")
End If

If rozdzielczosc = "1024x768" Then
ActiveWindow.Zoom = ("100")
End If

If rozdzielczosc = "1280x1024" Then
ActiveWindow.Zoom = ("125")
End If

Range("A1").Select

Arkusz1.Protect ([Haslo_uzytkownika])
Arkusz18.Protect ([Haslo_uzytkownika])
Range("A1").Select

End Sub

ZDARZENIA DLA ARKUSZA MENU-PODOBNEGO Kliknij, żeby (roz)winąć listing

Private Sub Worksheet_Activate()

Dim rozdzielczosc As String
Dim Haslo_uzytkownika As String

Haslo_uzytkownika = Arkusz17.Range("D2").Value
Arkusz18.Unprotect ([Haslo_uzytkownika])

'POBRANIE OBOWIĄZUJĄCYCH WARTOŚCI
rozdzielczosc = Arkusz17.Range("E24").Value

'USTAWIANIE ZOOMU I SZEROKOŚCI KOLUMNY A - MUSI BYĆ WARTOŚCIĄ STAŁĄ PRZY
'SPEŁNIONYM WARUNKU

If rozdzielczosc = "800x600" Then
ActiveWindow.Zoom = ("79")
End If

If rozdzielczosc = "1024x768" Then
ActiveWindow.Zoom = ("100")
End If

If rozdzielczosc = "1280x1024" Then
ActiveWindow.Zoom = ("125")
End If

Arkusz18.Protect ([Haslo_uzytkownika])

Range("A1").Select

Arkusz1.Select
End Sub

Aplikacja powinna wyglądać w sposób przewidziany przez jej autora w różnych rozdzielczościach ekranu. ze względu na fakt, że większość budowanych przez mnie programów operuje na własnym menu narysowanym w jednym z formularzy, jako dostosowywanie rozdzielczości wykorzystałem zmianę parametru 'Zoom' arkusza. Zmiana tego parametru w trakcie działania programu w zależności od zaznaczonej opcji wymaga obsługi zdarzeń dla arkusza. Przykład zaczerpnięty został z jednego z programów.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Obliczenia księgowe w Excelu - Dlaczego suma złotówek się nie zgadza? KLIKNIJ by (roz)winąć

Częsty problem dla osób, które w MS Excel liczą wartości księgowe - dlaczego po wyliczeniu i zsumowaniu kilkunastu kolumn np. z iloczynem procent x kwota suma końcowa kwot nie zgadza się z tym co mam na kalkulatorze? Diabeł tkwi w szczegółach - fakt, że użytkownik nakazał wyświetlać liczby z dokładnością do dwóch miejsc po przecinku nie znaczy wcale, że Excel dokonuje obliczeń z taką dokładnością. Problem nie zawsze wychodzi przy dodawaniu, ale przy mnożeniu już tak - nadal jesteśmy uszczęśliwiani dokładnością kilkunastu miejsc po przecinku, co po zsumowaniu da różnice w groszach - czyli dramat dla księgowych. Takie jest chyba uzasadnienie dla obrazka, jaki czasami widujemy - użytkownik przed komputerem z tabelą w Excelu sprawdza na kalkulatorze czy komputer dobrze policzył ;) W tradycyjnym Excelu problem jest prosty do załatwienia - ustawiamy wyświetlanie liczb dla pliku do dwóch miejsc po przecinku i teraz kolejno menu narzędzia/opcje/zakładka 'przeliczanie'/zaznaczamy 'dokładność taka, jak wyświetlono'- Excel ostrzega, że dokładność danych zostanie trwale utracona - zgadzamy się i ... możemy odłożyć kalkulator - Excel będzie liczył prawidłowo ;) Problem jest załatwiony na poziomie Excela, ale na poziomie VBA nadal istnieje - natknąłem sie na niego przy okazji programu WYKUP GRUNTÓW. Istotą programu jest liczydło, które za chwilę omówię. Liczydło uruchamiane jest z formularza jak na ilustracji. Formularz wykup gruntówKilka słów wstępu: jednostka administracyjna, w której pracuję składa się z siedmiu nieruchomości budynkowych. Spółdzielnia wykupuje grunt pod tymi budynkami od miasta. Każdy mieszkaniec, który chce ustanowić pełną własność, wyodrębnić własność, sprzedać lokal itp. jest tym zainteresowany. Całkowitą kwotę gruntu dla nieruchomości podzielono przez sumę powierzchni wszystkich lokali. Wykup gruntów rozłożono na raty miesięczne - różne dla różnych nieruchomości. W efekcie w pewnych przedziałach czasu ustalono stawki jednostkowe za metr powierzchni mieszkania np. od października 2008 do grudnia 2009 stawka za metr mieszkania w nieruchomości X wynosi 1,0 zł, a od stycznia 2010 do lutego 2013 stawka za metr tego samego mieszkania wynosi 0,75 zł (kwoty przykładowe). W marcu 2013 mieszkańcy są już pełnymi właścicielami gruntu pod swoimi budynkami. Jeżeli użytkownik lokalu chce go np. sprzedać w trakcie okresu spłaty rat za grunt to zobowiązany jest pokryć jednorazowo resztę rat od momentu wykupu. Wystarczy więc wiedzieć od jakiego miesiąca następuje wykup i znać powierzchnię mieszkania - obliczenia są proste. W Excelu zbudowałem tabelę: w pierwszej kolumnie adres lokalu, w drugiej jego powierzchnia, dalej w pięćdziesięciu czterech kolumnach stawki dla poszczególnych miesięcy. Zadaniem algorytmu byłoby dla odpowiedniego wiersza (wyróżnikiem jest adres lokalu) zsumowanie stawek od właściwej dla zaznaczonego miesiąca kolumny i wyznaczenie kwoty całkowitej w zależności od powierzchni mieszkania. Realizowany był wzór:
Kwota = (∑ stawek_jednostkowych_od_zadanego_miesiąca) * powierzchnia_lokalu
Aby program nie był tylko liczydłem wprowadziłem możliwość druku zaświadczenia dla lokalu, a ponieważ takie zaświadczenie staje się dokumentem finansowym również opcję sprawdzenia czy zaświadczenie dla danego lokalu nie było już wcześniej wystawiane oraz wydruk rejestru wystawionych zaświadczeń dla lokali z kwotami, datami uregulowania należności i w podziale na nieruchomości. Program dostosowałem do pracy w sieci. Nie zmienia to faktu, że zaproponowany produkt jest po prostu nieco bardziej zaawansowanym liczydełkiem. Obliczenia są uruchamiane przez wybór miesiąca z listy rozwijanej na widocznym formularzu. Obsługujący program po prostu wprowadza w oknie tekstowym adres, program odnajduje powierzchnię, którą wyświetla w drugim oknie i odblokowuje listę rozwijaną miesięcy. Zmiana tej listy (zdarzenie ComboBox1_Change) uruchamia proces obliczeń i wyświetlenia wyników w etykietach formularza (Label).
Pierwszym co usłyszałem po przekazaniu programu do stosowania było 'Twój program coś źle liczy'.
No to był cios...
Jak już doszedłem do siebie szukałem co jest nie tak i wylazło że matematyka księgowa różni się od tej ogólnej, a wzór powinien wyglądać tak:
Kwota = ∑ (stawka_jednostkowa_od_zadanego_miesiąca * powierzchnia_lokalu)
Tym samym dział księgowości dokonał rewolucji w matematyce unieważniając prawo rozdzielności mnożenia względem dodawania - w co tu teraz wierzyć? ;) Tak poważniej to różnica wynikała z zaokrągleń - w pierwszym wzorze zaokrąglana jest końcowa kwota, w drugim wzorze zaokrąglana jest każda z kwot cząstkowych miesięcznych.
Pełen listing dla zdarzenia ComboBox1_Change będącego starterem dla obliczeń zamieszczam poniżej: Kolorem zaznaczyłem najważniejszy w tym punkcie fragment tj. pętlę For - Next. Zaokrąglanie obliczanej wartości do dwóch miejsc po przecinku to polecenie 'Format(Zaokrąglana_zmienna, "0.#0")'. Zaokrąglana zmienna może być, tak jak w listingu, podana wzorem. Dla osób bardziej zainteresowanych formatami zapisu danych - również daty i czasu polecam książkę pana Arkadiusza Wilczyńskiego 'Leksykon Visual Basic', zawierającą bardzo praktyczne w stosowaniu tabele.

UWAGA - Polecam również tematy na blogu p. Marcina Egerta - tutaj i tutaj.

Jeśli jesteś zainteresowany tematem uzyskania kwot słownie w excelu i makrem do ściągnięcia to oprócz tematów w tym dziale polecam artykuł z działu ZAGADNIENIA VBA - Kwota słownie w Excelu.

UWAGA:
Przygotowałem samodzielną wtyczkę rozszerzającą możliwości Excela między innymi o zapis słowny kwoty - opis na stronie Dodatek_GK. Zastosowanie wtyczki nie wymaga umiejętności programowania.

STARTER OBLICZEŃ WARTOŚCI WYKUPU Kliknij, żeby (roz)winąć listing

Private Sub ComboBox1_Change()

Dim s As String
Dim jest As Range
Dim Ozn_nieruch As String
Dim Data_1 As Single
Dim Ozn_data As Range
Dim i As Integer, Licznik_pomoc As Integer, a_1 As Integer, j As Integer
Dim Suma As Single
'ccccccccccccccccccccccccccccc
Dim Powierzchnia As Single, Stawka_ula As Single, Iloczyn As Single
'ccccccccccccccccccccccccccccc

Set jest = Arkusz2.Range("B1")
Powierzchnia = 0

If Arkusz4.Range("B17").Value = "Praca" Then

s = TextBox1.Value
If s = "1" Or s = "2" Or s = "3" Or s = "4" Or s = "5" Or s = "6" Or s = "7" Then s = ""

Set jest = Arkusz2.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
If Not jest Is Nothing Then

With jest

TextBox2.Value = jest.Offset(0, 2).Value
'ccccccccccccccccccccccccccccccccccccccccc

Powierzchnia = CCur(Format(jest.Offset(0, 2).Value, "0.#0"))
'Powierzchnia = jest.Offset(0, 2).Value
'ccccccccccccccccccccccccccccccccccccccccc
Arkusz4.Range("B30").Value = jest.Offset(0, 81).Value

Ozn_nieruch = ""
Data_1 = 0

Set Ozn_data = Arkusz2.Range("F2800")

'Określenie zmiennej nieruchomości
Ozn_nieruch = Arkusz4.Range("B30").Value

'Określenie zmiennych dat
Licznik_pomoc = 3

For i = 2800 To 2856
If Ozn_data.Value = UserForm1.ComboBox1.Value Then
Data_1 = Ozn_data.Offset(0, -1).Value
a_1 = Licznik_pomoc
End If

Set Ozn_data = Ozn_data.Offset(1, 0)
Licznik_pomoc = Licznik_pomoc + 1
Next

Suma = 0

For j = a_1 To 57
Suma = Suma + jest.Offset(0, j).Value
'cccccccccccccccccccccccccccccccccccc
Stawka_ula = Format((jest.Offset(0, j).Value), "0.#0")
Iloczyn = Iloczyn + Format(Stawka_ula * Powierzchnia, "0.#0")
'ccccccccccccccccccccccccccccccccccccc
Next

Arkusz4.Range("B21").Value = Suma

UserForm1.Label4.Caption = Arkusz4.Range("B21").Value

CommandButton3.Enabled = True

If Ozn_nieruch = "Nieruchomość 1" And Arkusz3.Range("C6").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 2" And Arkusz3.Range("C7").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 3" And Arkusz3.Range("C8").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 4" And Arkusz3.Range("C9").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 5" And Arkusz3.Range("C10").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 6" And Arkusz3.Range("C11").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

If Ozn_nieruch = "Nieruchomość 7" And Arkusz3.Range("C12").Value = "N/D" Then
UserForm1.Label4.Caption = "b/d"
UserForm1.Label5.Caption = "b/d"
CommandButton3.Enabled = False
End If

End With

On Error GoTo Et:

If Label4.Caption <> "b/d" Then
'Label5.Caption = jest.Offset(0, 2).Value * Arkusz4.Range("B21").Value
'cccccccccccccccccccccccccccc
Label5.Caption = Format(Iloczyn, "0.#0")
'cccccccccccccccccccccccccccc
End If

Label3.Enabled = True
TextBox2.Enabled = True
CommandButton1.Enabled = False

Else
i = MsgBox("Do tej pory adresu: " + s + " nie było na Osiedlu Rusa, ale:" & Chr(10) & "'Gdyby otworzono
drzwi percepcji wszystkie rzeczy przedstawiałyby sie człowiekowi takimi, jakie sa naprawdę: jako nieskończone'
/W.Blake/" & Chr(10) & "więc może spróbuj jeszcze raz :)", vbOKOnly + vbCritical, "Uważaj!")

'Ustawienia początkowe
Label1.Enabled = True
TextBox1.Enabled = True
Arkusz4.Range("B17").Value = "Zmiana"
TextBox1.Value = ""
ComboBox1.Value = ""
ComboBox1.Enabled = False
Label2.Enabled = False
Label3.Enabled = False
TextBox2.Enabled = False
TextBox2.Value = ""
CommandButton1.Enabled = False
CommandButton2.Enabled = True
Label4.Caption = "XXXXXX"
Label5.Caption = "XXXXXX"
Arkusz4.Range("B17").Value = "Praca"

CommandButton3.Enabled = False

End If

End If

Exit Sub

Et: Label5.Caption = "XXXXX"

End Sub

W punkcie przedstawiłem częsty problem podczas obliczeń wartości finansowych w Excelu - jak uzyskać zaokrąglenia i dokładnośc obliczeń zgodną z oczekiwaną. Przykład przedstawiany w tym punkcie pochodzi z jednego z moich programów. Sądzę, że prześledzenie sposobu wprowadzenia zaokrągleń i kolejności operacji w przyjętym algorytmie może być wielu osobom przydatne.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wygodny formularz VBA - właściwość Visible KLIKNIJ by (roz)winąć

Spośród kilku różnych programów, jako przykład wybrałem projekt WYKUP GRUNTÓW.Formularz wykup gruntów Jest to swego rodzaju kontynuacja uwagi powyżej. Zaprezentowany tam formularz zawiera przycisk CommandButton o nazwie (Caption) 'Podgląd wydruku'. Po kliknięciu na nim program wyświetli formularz jak na ilustracji. Jego zawartość częściowo zależy od wartości poszczególnych formantów na formularzu wywołującym. To na co chciałbym zwrócić uwagę to etykiety (Label) w kolorze czerwonym, które mogą pojawić się w prawym górnym rogu formularza. Etykiety te ostrzegają użytkownika przed wydrukiem zaświadczenia dla lokalu, który już jedno zaświadczenie otrzymał (druk ten po podpisaniu przez kierownika jednostki staje się dokumentem finansowym). Co ważne dla spokoju użytkownika programu - komunikat ten pojawi się (Labelx.Visible = True) tylko gdy jest potrzebny. Formularz wykup gruntówRealizacja tej funkcji następuje przez odpowiednią inicjalizację formularza (inicjalizacją są działania wykonywane jeszcze przed wyświetleniem formularza na ekranie) z wywołaniem podprocedury z modułu. Na osobnej ilustracji przedstawiam również etykiety analizowanego formularza jako zrzut ekranu z poziomu edytora VBA. Dla lepszego zrozumienia przedstawianego zagadnienia dobrze jest zapoznać się z ideą działania programu przedstawioną pokrótce w uwadze powyżej. Poniżej listingi: Warto zwrócić uwagę, że przy inicjalizacji formularza komunikat nie pojawia się nigdy - właściwość Visible odpowiednich etykiet określona jest jako 'False'. Dopiero podprocedura określa widoczność tych etykiet w sytuacji gdy sprawdzany warunek jest spełniony.
Inny przykład wykorzystania właściwości Visible formantów zawarty jest w uwadze 'Program wygodny w użytkowaniu?' pochodzącej z programu REJESTR WODOMIERZY.

INICJALIZACJA FORMULARZA Kliknij, żeby (roz)winąć listing

Private Sub UserForm_Initialize()

Dim Combo As Range

'Dane wyjściowe
Label1.Caption = Arkusz6.Range("B3").Value
TextBox1.Value = Arkusz4.Range("L6").Value
TextBox2.Value = Arkusz4.Range("L6").Value
Label2.Caption = Arkusz6.Range("B7").Value
Label3.Caption = Arkusz6.Range("B8").Value
Label4.Caption = UserForm1.TextBox1.Value
Label5.Caption = Arkusz6.Range("E8").Value

'Label6.Caption
Set Combo = Arkusz2.Range("F2799")

Do
Set Combo = Combo.Offset(1, 0)
Loop Until Combo.Value = UserForm1.ComboBox1.Value

Label6.Caption = Combo.Offset(-1, 2).Value

If Label6.Caption = "" Then Label6.Caption = "..............."

Label7.Caption = Arkusz6.Range("D9").Value
Label8.Caption = UserForm1.Label5.Caption & " " & "zł"
Label9.Caption = Arkusz6.Range("B10").Value

'Komunikat
Label10.Visible = False
Label11.Visible = False
Label12.Visible = False
Label13.Visible = False

Call Komunikat

End Sub

PROCEDURA KOMUNIKAT Kliknij, żeby (roz)winąć listing

Sub Komunikat()

Dim s As String
Dim jest As Range
Dim Ozn_nieruch As String
Dim Data_1 As Single
Dim Ozn_data As Range
Dim i As Integer, Licznik_pomoc As Integer, a_1 As Integer, j As Integer
Dim Suma As Single
'ccccccccccccccccccccccccccccc
Dim Powierzchnia As Single, Stawka_ula As Single, Iloczyn As Single
'ccccccccccccccccccccccccccccc

Set jest = Arkusz2.Range("B1")
Powierzchnia = 0

If Arkusz4.Range("B17").Value = "Praca" Then

s = UserForm1.TextBox1.Value
If s = "1" Or s = "2" Or s = "3" Or s = "4" Or s = "5" Or s = "6" Or s = "7" Then s = ""

Set jest = Arkusz2.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
If Not jest Is Nothing Then

With jest

End With

On Error GoTo Et:

If jest.Offset(0, 84).Value <> "" Then
'Label5.Caption = jest.Offset(0, 2).Value * Arkusz4.Range("B21").Value
'cccccccccccccccccccccccccccc
UserForm6.Label10.Visible = True
UserForm6.Label11.Caption = jest.Offset(0, 85).Value
UserForm6.Label11.Visible = True
UserForm6.Label12.Visible = True
UserForm6.Label13.Caption = jest.Offset(0, 84).Value
UserForm6.Label13.Visible = True
'cccccccccccccccccccccccccccc
End If

Else
i = MsgBox("Do tej pory adresu: " + s + " nie było na Osiedlu Rusa, ale:" & Chr(10) & "'Gdyby otworzono drzwi percepcji wszystkie rzeczy przedstawiałyby sie człowiekowi takimi, jakie sa naprawdę: jako nieskończone' /W.Blake/" & Chr(10) & "więc może spróbuj jeszcze raz :)", vbOKOnly + vbCritical, "Uważaj!")

End If

End If

Exit Sub

Et:
End Sub

Jedną z ciekawszych i dających duże możliwości własciwością kontrolek i niektórych obiektów Excela np. arkuszy jest właściwość Visible - widoczność. W tym punkcie zasygnalizowane są możliwości poprawy funkcjonalności kontrolek dzięki zastosowaniu tej właściwości. Na prezentowanym formularzu - pochodzącym z rzeczywiście eksploatowanego programu - zastosowano dwókrotnie większą ilość kontrolek niż jest w danej chwili widoczne. Inny punkt dotyczący tego zagadnienia przedstawiłem w dziale ZAGADNIENIA VBA - Ukrywanie formantów VBA.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wygodny formularz VBA - właściwość ColorFont i Enabled formantów KLIKNIJ by (roz)winąć

Przykładem będzie formularz wprowadzania danych zaczerpnięty z programu OKNA EWIDENCJA. Jest to mój pierwszy duży program więc nie doszedłem jeszcze wtedy do, między innymi, odpowiedniego nazewnictwa formantów, a ponieważ wszystko działa nie mam zacięcia, żeby to poprawić. Formularz wprowadź dane okienNa ilustracji formularz po wprowadzeniu i zapisaniu danych dla lokalu. Działanie formularza wygląda następująco: po kliknięciu LPM na przycisku 'Wprowadź dane mieszkania' menu głównego programu i pozytywnej weryfikacji hasła program wyświetla formularz wprowadzania danych. Przy uruchomieniu jest on zablokowany. Po wybraniu lokalu za pośrednictwem przycisku CommandButton 'Następny lokal' następuje wyszukanie danych. Tutaj proszę zwrócić uwagę, w kontekście tego punktu, na działanie formularza - wszystkie etykiety z nazwami okien oraz odpowiadające im pola tekstowe, które nie dotyczą danego lokalu zostaną zablokowane - właściwość Enabled = False. Spośród wszystkich jedenastu możliwych zestawów okiennych osiedla, najwyżej sześć jest w mieszkaniu (najczęściej cztery). Pozostałe etykiety i pola tekstowe utrudniałyby obsługę i zwiększałyby możliwość popełnienia błędów przez użytkownika. Warunkiem, które powoduje, że dane pole jest zablokowane jest znak 'X' w głównej tabeli, z której pobierane i do której zapisywane są dane. Podczas użytkowania programu często zdarzało się, że po wciśnięciu przycisku 'Zapisz dane' program dane owszem zapisywał, tyle że na formularzu nic się nie zmieniało. Niby mały problem, ale wciskając kilka razy ten sam przycisk użytkownik odczuwa jednak pewien dyskomfort. Wprowadzona zmiana jest równie niewielka co przydatna - po zapisie program zmienia kolor napisu na przycisku - funkcja CommandButton.ForeColor = 'x'. Przywrócenie standardowego koloru uzyskiwane jest przez ponowne użycie przycisku 'Następny lokal'. Można również przycisk zapisu po wykonaniu operacji zablokować - kwestia gustu.
Teraz opis formantów (skoro już nie wpadłem na to żeby je nazwać po ludzku - niech to będzie przykład negatywny na potwierdzenie uwagi 'Od czego rozpocząć programowanie?'):
- Przycisk 'Zapisz dane' - CommandButton1,
- Przycisk 'Następny lokal' - CommandButton2,
- Przycisk 'Wyjdź' - CommandButton3,
- Etykieta 'Os. Rusa' - Label14,
- Etykieta z adresem - Label15,
- Etykiety z nazwami okien od góry: Label1, 4,5,6,7,8,9,10,11,12,13,
- Etykieta z rodzajem działań: Label3,
- Etykieta z datą działań: Label2,
- Okna tekstowe z rodzajem działań od góry: TextBox1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21,
- Okna tekstowe z datą działań od góry: TextBox2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
Poniżej listingi dla zdarzeń na formularzu. Kilka uwag ogólnych - zmienne jednego typu można zapisać w jednej linijce, nie tak jak w przykładzie (z tym że mnie się lepiej sprawdzało działanie programu przy takim zapisie). Przy pętli 'If .. Then' jeżeli jest tylko jedna linijka polecenia to może być zapisana w jednej linii z 'Then', jeżeli tych poleceń jest więcej to muszą być wprowadzane począwszy od następnej linijki po słowie 'Then'. W rzeczywistości typów okien na osiedlu jest dwanaście, z tym że 'drugie okno O-39 wschód' znajduje się tylko w jedenastu lokalach i stąd zamiast wprowadzać osobny zapis, na formularzu sprawdzane są warunki i następuje podmiana etykiety 'balkon na załamaniu' - w tabeli źródłowej obydwa dość nietypowe zestawy zapisywane są w jednej kolumnie. Działanie pętli warunkowej 'If Then Else', w której po 'Else' następuje wyświetlenie MsgBox 'Błąd działania w oknie typu ... Działanie nie zostało zapisane' widoczne jest na ilustracji w uwadze Program VBA bezpieczny w pracy.

NAJPIERW FORMULARZ - Kliknij, żeby (roz)winąć listing

Private Sub UserForm_Activate()

'Ustawienia początkowe
Label14.Caption = "Wprowadź"
Label15.Caption = "adres"
CommandButton1.Enabled = False
TextBox1.Value = ""
TextBox2.Value = ""
TextBox1.Enabled = False
TextBox2.Enabled = False
Label1.Enabled = False
TextBox3.Value = ""
TextBox4.Value = ""
TextBox3.Enabled = False
TextBox4.Enabled = False
Label4.Enabled = False
TextBox5.Value = ""
TextBox6.Value = ""
TextBox5.Enabled = False
TextBox6.Enabled = False
Label5.Enabled = False
TextBox7.Value = ""
TextBox8.Value = ""
TextBox7.Enabled = False
TextBox8.Enabled = False
Label6.Enabled = False
TextBox9.Value = ""
TextBox10.Value = ""
TextBox9.Enabled = False
TextBox10.Enabled = False
Label7.Enabled = False
TextBox11.Value = ""
TextBox12.Value = ""
TextBox11.Enabled = False
TextBox12.Enabled = False
Label8.Enabled = False
TextBox13.Value = ""
TextBox14.Value = ""
TextBox13.Enabled = False
TextBox14.Enabled = False
Label9.Enabled = False
TextBox15.Value = ""
TextBox16.Value = ""
TextBox15.Enabled = False
TextBox16.Enabled = False
Label10.Enabled = False
Label10.Caption = "Zastaw balkon na załamaniu OB7/8+O-33"
TextBox17.Value = ""
TextBox18.Value = ""
TextBox17.Enabled = False
TextBox18.Enabled = False
Label11.Enabled = False
TextBox19.Value = ""
TextBox20.Value = ""
TextBox19.Enabled = False
TextBox20.Enabled = False
Label12.Enabled = False
TextBox21.Value = ""
TextBox22.Value = ""
TextBox21.Enabled = False
TextBox22.Enabled = False
Label13.Enabled = False
End Sub

Private Sub UserForm_Initialize()

'Ustawienia początkowe
Label14.Caption = "Wprowadź"
Label15.Caption = "adres"
CommandButton1.Enabled = False
TextBox1.Value = TextBox2.Value = ""
TextBox1.Enabled = False
TextBox2.Enabled = False
Label1.Enabled = False
TextBox3.Value = TextBox4.Value = ""
TextBox3.Enabled = False
TextBox4.Enabled = False
Label4.Enabled = False
TextBox5.Value = TextBox6.Value = ""
TextBox5.Enabled = False
TextBox6.Enabled = False
Label5.Enabled = False
TextBox7.Value = TextBox8.Value = ""
TextBox7.Enabled = False
TextBox8.Enabled = False
Label6.Enabled = False
TextBox9.Value = TextBox10.Value = ""
TextBox9.Enabled = False
TextBox10.Enabled = False
Label7.Enabled = False
TextBox11.Value = TextBox12.Value = ""
TextBox11.Enabled = False
TextBox12.Enabled = False
Label8.Enabled = False
TextBox13.Value = TextBox14.Value = ""
TextBox13.Enabled = False
TextBox14.Enabled = False
Label9.Enabled = False
TextBox15.Value = TextBox16.Value = ""
TextBox15.Enabled = False
TextBox16.Enabled = False
Label10.Enabled = False
Label10.Caption = "Zastaw balkon na załamaniu OB7/8+O-33"
TextBox17.Value = TextBox18.Value = ""
TextBox17.Enabled = False
TextBox18.Enabled = False
Label11.Enabled = False
TextBox19.Value = TextBox20.Value = ""
TextBox19.Enabled = False
TextBox20.Enabled = False
Label12.Enabled = False
TextBox21.Value = TextBox22.Value = ""
TextBox21.Enabled = False
TextBox22.Enabled = False
Label13.Enabled = False
End Sub

TERAZ PRZYCISKI - Kliknij, żeby (roz)winąć listing

Private Sub CommandButton1_Click()

Dim a1 As String, a2 As String, b1 As String, b2 As String, c1 As String, c2 As String, d1 As Stri, d2 As String, e1 As String, e2 As String, f1 As String, f2 As String, g1 As String, g2 As String, h1 As String, h2 As String, i1 As String, i2 As String, j1 As String, j2 As String, k1 As String, k2 As String, s As String
Dim i As Integer
Dim jest As Range
s = Label15.Caption
Set jest = Arkusz3.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
If Not jest Is Nothing Then
With jest
a1 = .Offset(0, 3).Value
a2 = .Offset(0, 4).Value
b1 = .Offset(0, 5).Value
b2 = .Offset(0, 6).Value
c1 = .Offset(0, 7).Value
c2 = .Offset(0, 8).Value
d1 = .Offset(0, 9).Value
d2 = .Offset(0, 10).Value
e1 = .Offset(0, 11).Value
e2 = .Offset(0, 12).Value
f1 = .Offset(0, 13).Value
f2 = .Offset(0, 14).Value
g1 = .Offset(0, 15).Value
g2 = .Offset(0, 16).Value
h1 = .Offset(0, 17).Value
h2 = .Offset(0, 18).Value
i1 = .Offset(0, 19).Value
i2 = .Offset(0, 20).Value
j1 = .Offset(0, 21).Value
j2 = .Offset(0, 22).Value
k1 = .Offset(0, 23).Value
k2 = .Offset(0, 24).Value
End With
End If

'OKNO O-17
If TextBox1 = "n/d" Or TextBox1 = "w" Or TextBox1 = "e" Or TextBox1 = "ebk" Or TextBox1 = "k" Or TextBox1 = "ePK" Or TextBox1 = "wPK" Or TextBox1 = "BK" Or TextBox1 = "Z" Or TextBox1 = "" Then jest.Offset(0, 3).Value = TextBox1
If TextBox1 = "n/d" Then jest.Offset(0, 3).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-17. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox1 = "n/d" Or TextBox1 = "w" Or TextBox1 = "e" Or TextBox1 = "ebk" Or TextBox1 = "k" Or TextBox1 = "ePK" Or TextBox1 = "wPK" Or TextBox1 = "BK" Or TextBox1 = "Z" Or TextBox1 = "" Then jest.Offset(0, 4).Value = TextBox2
If TextBox1 = "n/d" Then jest.Offset(0, 4).Value = "X"
End If

'OKNO O-33
If TextBox3 = "n/d" Or TextBox3 = "w" Or TextBox3 = "e" Or TextBox3 = "ebk" Or TextBox3 = "k" Or TextBox3 = "ePK" Or TextBox3 = "wPK" Or TextBox3 = "BK" Or TextBox3 = "Z" Or TextBox3 = "" Or TextBox3 = "n/d" Then
jest.Offset(0, 5).Value = TextBox3
If TextBox3 = "n/d" Then jest.Offset(0, 5).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-33. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox3 = "n/d" Or TextBox3 = "w" Or TextBox3 = "e" Or TextBox3 = "ebk" Or TextBox3 = "k" Or TextBox3 = "ePK" Or TextBox3 = "wPK" Or TextBox3 = "BK" Or TextBox3 = "Z" Or TextBox3 = "" Or TextBox3 = "n/d" Then
jest.Offset(0, 6).Value = TextBox4
If TextBox3 = "n/d" Then jest.Offset(0, 6).Value = "X"
End If

'OKNO O-17+O-3
If TextBox5 = "n/d" Or TextBox5 = "w" Or TextBox5 = "e" Or TextBox5 = "ebk" Or TextBox5 = "k" Or TextBox5 = "ePK" Or TextBox5 = "wPK" Or TextBox5 = "BK" Or TextBox5 = "Z" Or TextBox5 = "" Or TextBox5 = "n/d" Then
jest.Offset(0, 7).Value = TextBox5
If TextBox5 = "n/d" Then jest.Offset(0, 7).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-17+O-3. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox5 = "n/d" Or TextBox5 = "w" Or TextBox5 = "e" Or TextBox5 = "ebk" Or TextBox5 = "k" Or TextBox5 = "ePK" Or TextBox5 = "wPK" Or TextBox5 = "BK" Or TextBox5 = "Z" Or TextBox5 = "" Or TextBox5 = "n/d" Then
jest.Offset(0, 8).Value = TextBox6
If TextBox5 = "n/d" Then jest.Offset(0, 8).Value = "X"
End If

'OKNO O-38/O-39 wschód
If TextBox7 = "n/d" Or TextBox7 = "w" Or TextBox7 = "e" Or TextBox7 = "ebk" Or TextBox7 = "k" Or TextBox7 = "ePK" Or TextBox7 = "wPK" Or TextBox7 = "BK" Or TextBox7 = "Z" Or TextBox7 = "" Or TextBox7 = "n/d" Then
jest.Offset(0, 9).Value = TextBox7
If TextBox7 = "n/d" Then jest.Offset(0, 9).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-38/O-39 wschód. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox7 = "n/d" Or TextBox7 = "w" Or TextBox7 = "e" Or TextBox7 = "ebk" Or TextBox7 = "k" Or TextBox7 = "ePK" Or TextBox7 = "wPK" Or TextBox7 = "BK" Or TextBox7 = "Z" Or TextBox7 = "" Or TextBox7 = "n/d" Then
jest.Offset(0, 10).Value = TextBox8
If TextBox7 = "n/d" Then jest.Offset(0, 10).Value = "X"
End If

'OKNO O-38/O-39 zachód
If TextBox9 = "n/d" Or TextBox9 = "w" Or TextBox9 = "e" Or TextBox9 = "ebk" Or TextBox9 = "k" Or TextBox9 = "ePK" Or TextBox9 = "wPK" Or TextBox9 = "BK" Or TextBox9 = "Z" Or TextBox9 = "" Or TextBox9 = "n/d" Then
jest.Offset(0, 11).Value = TextBox9
If TextBox9 = "n/d" Then jest.Offset(0, 11).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-38/O-39 zachód. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox9 = "n/d" Or TextBox9 = "w" Or TextBox9 = "e" Or TextBox9 = "ebk" Or TextBox9 = "k" Or TextBox9 = "ePK" Or TextBox9 = "wPK" Or TextBox9 = "BK" Or TextBox9 = "Z" Or TextBox9 = "" Or TextBox9 = "n/d" Then
jest.Offset(0, 12).Value = TextBox10
If TextBox9 = "n/d" Then jest.Offset(0, 12).Value = "X"
End If

'OKNO O-38/O-39 kuchnia
If TextBox11 = "n/d" Or TextBox11 = "w" Or TextBox11 = "e" Or TextBox11 = "ebk" Or TextBox11 = "k" Or TextBox11 = "wPK" Or TextBox11 = "ePK" Or TextBox11 = "BK" Or TextBox11 = "Z" Or TextBox11 = "" Or TextBox11 = "n/d" Then
jest.Offset(0, 13).Value = TextBox11
If TextBox11 = "n/d" Then jest.Offset(0, 13).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w oknie O-38/O-39 kuchnia. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox11 = "n/d" Or TextBox11 = "w" Or TextBox11 = "e" Or TextBox11 = "ebk" Or TextBox11 = "k" Or TextBox11 = "wPK" Or TextBox11 = "ePK" Or TextBox11 = "BK" Or TextBox11 = "Z" Or TextBox11 = "" Or TextBox11 = "n/d" Then
jest.Offset(0, 14).Value = TextBox12
If TextBox11 = "n/d" Then jest.Offset(0, 14).Value = "X"
End If

'ZESTAW OB5/6+O-34/O-35
If TextBox13 = "n/d" Or TextBox13 = "w" Or TextBox13 = "e" Or TextBox13 = "ebk" Or TextBox13 = "k" Or TextBox13 = "wPK" Or TextBox13 = "ePK" Or TextBox13 = "BK" Or TextBox13 = "Z" Or TextBox13 = "" Or TextBox13 = "n/d" Then
jest.Offset(0, 15).Value = TextBox13
If TextBox13 = "n/d" Then jest.Offset(0, 15).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w zestawie OB5/6 + O-34/O-35. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox13 = "n/d" Or TextBox13 = "w" Or TextBox13 = "e" Or TextBox13 = "ebk" Or TextBox13 = "k" Or TextBox13 = "wPK" Or TextBox13 = "ePK" Or TextBox13 = "BK" Or TextBox13 = "Z" Or TextBox13 = "" Or TextBox13 = "n/d" Then
jest.Offset(0, 16).Value = TextBox14
If TextBox13 = "n/d" Then jest.Offset(0, 16).Value = "X"
End If

'ZESTAW OB7/8 + O-33
If TextBox15 = "n/d" Or TextBox15 = "w" Or TextBox15 = "e" Or TextBox15 = "ebk" Or TextBox15 = "k" Or TextBox15 = "wPK" Or TextBox15 = "ePK" Or TextBox15 = "BK" Or TextBox15 = "Z" Or TextBox15 = "" Or TextBox15 = "n/d" Then
jest.Offset(0, 17).Value = TextBox15
If TextBox15 = "n/d" Then jest.Offset(0, 17).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w zestawie OB7/8 + O-33 /lub O-39 drugie zachód/. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox15 = "n/d" Or TextBox15 = "w" Or TextBox15 = "e" Or TextBox15 = "ebk" Or TextBox15 = "k" Or TextBox15 = "wPK" Or TextBox15 = "ePK" Or TextBox15 = "BK" Or TextBox15 = "Z" Or TextBox15 = "" Or TextBox15 = "n/d" Then
jest.Offset(0, 18).Value = TextBox16
If TextBox15 = "n/d" Then jest.Offset(0, 18).Value = "X"
End If

'ZESTAW OB7/8 + O-34/O-35
If TextBox17 = "n/d" Or TextBox17 = "w" Or TextBox17 = "e" Or TextBox17 = "ebk" Or TextBox17 = "k" Or TextBox17 = "wPK" Or TextBox17 = "ePK" Or TextBox17 = "BK" Or TextBox17 = "Z" Or TextBox17 = "" Or TextBox17 = "n/d" Then
jest.Offset(0, 19).Value = TextBox17
If TextBox17 = "n/d" Then jest.Offset(0, 19).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w zestawie OB7/8 + O-34/O-35. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If
If TextBox17 = "n/d" Or TextBox17 = "w" Or TextBox17 = "e" Or TextBox17 = "ebk" Or TextBox17 = "k" Or TextBox17 = "wPK" Or TextBox17 = "ePK" Or TextBox17 = "BK" Or TextBox17 = "Z" Or TextBox17 = "" Or TextBox17 = "n/d" Then
jest.Offset(0, 20).Value = TextBox18
If TextBox17 = "n/d" Then jest.Offset(0, 20).Value = "X"
End If

'ZESTAW OB7/8 + O-38/O-39
If TextBox19 = "n/d" Or TextBox19 = "w" Or TextBox19 = "e" Or TextBox19 = "ebk" Or TextBox19 = "k" Or TextBox19 = "wPK" Or TextBox19 = "ePK" Or TextBox19 = "BK" Or TextBox19 = "Z" Or TextBox19 = "" Or TextBox19 = "n/d" Then
jest.Offset(0, 21).Value = TextBox19
If TextBox19 = "n/d" Then jest.Offset(0, 21).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w zestawie OB7/8 + O-38/O-39. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox19 = "n/d" Or TextBox19 = "w" Or TextBox19 = "e" Or TextBox19 = "ebk" Or TextBox19 = "k" Or TextBox19 = "wPK" Or TextBox19 = "ePK" Or TextBox19 = "BK" Or TextBox19 = "Z" Or TextBox19 = "" Or TextBox19 = "n/d" Then
jest.Offset(0, 22).Value = TextBox20
If TextBox19 = "n/d" Then jest.Offset(0, 22).Value = "X"
End If

'ZESTAW OB9/10 + O-47 + O-51
If TextBox21 = "n/d" Or TextBox21 = "w" Or TextBox21 = "e" Or TextBox21 = "ebk" Or TextBox21 = "k" Or TextBox21 = "wPK" Or TextBox21 = "ePK" Or TextBox21 = "BK" Or TextBox21 = "Z" Or TextBox21 = "" Or TextBox21 = "n/d" Then
jest.Offset(0, 23).Value = TextBox21
If TextBox21 = "n/d" Then jest.Offset(0, 23).Value = "X"
Else
i = MsgBox("Błąd rodzaju działania w zestawie OB9/10 + O-47 + O-51. Działanie nie zostało zapisane.", vbOKOnly, "OKNA EWIDENCJA")
End If

If TextBox21 = "n/d" Or TextBox21 = "w" Or TextBox21 = "e" Or TextBox21 = "ebk" Or TextBox21 = "k" Or TextBox21 = "wPK" Or TextBox21 = "ePK" Or TextBox21 = "BK" Or TextBox21 = "Z" Or TextBox21 = "" Or TextBox21 = "n/d" Then
jest.Offset(0, 24).Value = TextBox22
If TextBox21 = "n/d" Then jest.Offset(0, 24).Value = "X"
End If

CommandButton1.ForeColor = &H8000&
End Sub

Private Sub CommandButton2_Click()

Dim a1 As String, a2 As String, b1 As String, b2 As String, c1 As String, c2 As String, d1 As String, d2 As String, e1 As String, e2 As String, f1 As String, f2 As String, g1 As String, g2 As String, h1 As String, h2 As String, i1 As String, i2 As String, j1 As String, j2 As String, k1 As String, k2 As String, s As String
Dim i As Integer
Dim jest As Range

CommandButton1.ForeColor = &H80000012

'Ustawienia początkowe
Label14.Caption = "Os. Rusa " & ""
CommandButton1.Enabled = True
TextBox1.Value = TextBox2.Value = ""
TextBox1.Enabled = True
TextBox2.Enabled = True
Label1.Enabled = True
TextBox3.Value = TextBox4.Value = ""
TextBox3.Enabled = True
TextBox4.Enabled = True
Label4.Enabled = True
TextBox5.Value = TextBox6.Value = ""
TextBox5.Enabled = True
TextBox6.Enabled = True
Label5.Enabled = True
TextBox7.Value = TextBox8.Value = ""
TextBox7.Enabled = True
TextBox8.Enabled = True
Label6.Enabled = True
TextBox9.Value = TextBox10.Value = ""
TextBox9.Enabled = True
TextBox10.Enabled = True
Label7.Enabled = True
TextBox11.Value = TextBox12.Value = ""
TextBox11.Enabled = True
TextBox12.Enabled = True
Label8.Enabled = True
TextBox13.Value = TextBox14.Value = ""
TextBox13.Enabled = True
TextBox14.Enabled = True
Label9.Enabled = True
TextBox15.Value = TextBox16.Value = ""
TextBox15.Enabled = True
TextBox16.Enabled = True
Label10.Caption = "Zastaw balkon na załamaniu OB7/8+O-33"
Label10.Enabled = True
TextBox17.Value = TextBox18.Value = ""
TextBox17.Enabled = True
TextBox18.Enabled = True
Label11.Enabled = True
TextBox19.Value = TextBox20.Value = ""
TextBox19.Enabled = True
TextBox20.Enabled = True
Label12.Enabled = True
TextBox21.Value = TextBox22.Value = ""
TextBox21.Enabled = True
TextBox22.Enabled = True
Label13.Enabled = True
a1 = a2 = b1 = b2 = c1 = c2 = d1 = d2 = e1 = e2 = f1 = f2 = ""
g1 = g2 = h1 = h2 = i1 = i2 = j1 = j2 = k1 = k2 = s = ""

'jest = "" 'Arkusz2.Range("B7") '.CurrentRegion.Find(s, LookAt:=xlWhole)
s = InputBox("Podaj adres do wprowadzenia", "Wprowadzanie danych dla lokalu:", "1/2") 'Program coś pieprzy gdy s z zakresu od 1-6 /prawdopodobnie CurrentRegion/
' If s = "1" Or s = "2" Or s = "3" Or s = "4" Or s = "5" Or s = "6" Or s = "7" Then s = "100"
' Set jest = Arkusz3.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
If Not jest Is Nothing Then
With jest
a1 = .Offset(0, 3).Value
a2 = .Offset(0, 4).Value
b1 = .Offset(0, 5).Value
b2 = .Offset(0, 6).Value
c1 = .Offset(0, 7).Value
c2 = .Offset(0, 8).Value
d1 = .Offset(0, 9).Value
d2 = .Offset(0, 10).Value
e1 = .Offset(0, 11).Value
e2 = .Offset(0, 12).Value
f1 = .Offset(0, 13).Value
f2 = .Offset(0, 14).Value
g1 = .Offset(0, 15).Value
g2 = .Offset(0, 16).Value
h1 = .Offset(0, 17).Value
h2 = .Offset(0, 18).Value
i1 = .Offset(0, 19).Value
i2 = .Offset(0, 20).Value
j1 = .Offset(0, 21).Value
j2 = .Offset(0, 22).Value
k1 = .Offset(0, 23).Value
k2 = .Offset(0, 24).Value
If a1 = "X" Then TextBox1.Value = "n/d"
If a1 = "X" Then TextBox2.Value = "n/d"
If a1 = "X" Then TextBox1.Enabled = False
If a1 = "X" Then TextBox2.Enabled = False
If a1 = "X" Then Label1.Enabled = False
If a1 = "X" Then TextBox2.Value = "n/d"
If a1 <> "X" Then TextBox1.Value = a1
If a1 <> "X" Then TextBox2.Value = a2

If b1 = "X" Then TextBox3.Value = "n/d"
If b1 = "X" Then TextBox4.Value = "n/d"
If b1 = "X" Then TextBox3.Enabled = False
If b1 = "X" Then TextBox4.Enabled = False
If b1 = "X" Then Label4.Enabled = False
If b1 <> "X" Then TextBox3.Value = b1
If b1 <> "X" Then TextBox4.Value = b2

If c1 = "X" Then TextBox5.Value = "n/d"
If c1 = "X" Then TextBox6.Value = "n/d"
If c1 = "X" Then TextBox5.Enabled = False
If c1 = "X" Then TextBox6.Enabled = False
If c1 = "X" Then Label5.Enabled = False
If c1 <> "X" Then TextBox5.Value = c1
If c1 <> "X" Then TextBox6.Value = c2

If d1 = "X" Then TextBox7.Value = "n/d"
If d1 = "X" Then TextBox8.Value = "n/d"
If d1 = "X" Then TextBox7.Enabled = False
If d1 = "X" Then TextBox8.Enabled = False
If d1 = "X" Then Label6.Enabled = False
If d1 <> "X" Then TextBox7.Value = d1
If d1 <> "X" Then TextBox8.Value = d2

If e1 = "X" Then TextBox9.Value = "n/d"
If e1 = "X" Then TextBox10.Value = "n/d"
If e1 = "X" Then TextBox9.Enabled = False
If e1 = "X" Then TextBox10.Enabled = False
If e1 = "X" Then Label7.Enabled = False
If e1 <> "X" Then TextBox9.Value = e1
If e1 <> "X" Then TextBox10.Value = e2

If f1 = "X" Then TextBox11.Value = "n/d"
If f1 = "X" Then TextBox12.Value = "n/d"
If f1 = "X" Then TextBox11.Enabled = False
If f1 = "X" Then TextBox12.Enabled = False
If f1 = "X" Then Label8.Enabled = False
If f1 <> "X" Then TextBox11.Value = f1
If f1 <> "X" Then TextBox12.Value = f2
If g1 = "X" Then TextBox13.Value = "n/d"
If g1 = "X" Then TextBox14.Value = "n/d"
If g1 = "X" Then TextBox13.Enabled = False
If g1 = "X" Then TextBox14.Enabled = False
If g1 = "X" Then Label9.Enabled = False
If g1 <> "X" Then TextBox13.Value = g1
If g1 <> "X" Then TextBox14.Value = g2
If h1 = "X" Then TextBox15.Value = "n/d"
If h1 = "X" Then TextBox16.Value = "n/d"
If h1 = "X" Then TextBox15.Enabled = False
If h1 = "X" Then TextBox16.Enabled = False
If h1 = "X" Then Label10.Enabled = False
If s = "126/4" Or s = "126/8" Or s = "126/12" Or s = "126/16" Or s = "126/20" Or s = "126/24" Or s = "126/28" Or s = "126/32" Or s = "126/36" Or s = "126/40" Or s = "126/45" Then
Label10.Caption = "Okno O-39 drugie okno wschód"
End If
If h1 <> "X" Then TextBox15.Value = h1
If h1 <> "X" Then TextBox16.Value = h2
If i1 = "X" Then TextBox17.Value = "n/d"
If i1 = "X" Then TextBox18.Value = "n/d"
If i1 = "X" Then TextBox17.Enabled = False
If i1 = "X" Then TextBox18.Enabled = False
If i1 = "X" Then Label11.Enabled = False
If i1 <> "X" Then TextBox17.Value = i1
If i1 <> "X" Then TextBox18.Value = i2
If j1 = "X" Then TextBox19.Value = "n/d"
If j1 = "X" Then TextBox20.Value = "n/d"
If j1 = "X" Then TextBox19.Enabled = False
If j1 = "X" Then TextBox20.Enabled = False
If j1 = "X" Then Label12.Enabled = False
If j1 <> "X" Then TextBox19.Value = j1
If j1 <> "X" Then TextBox20.Value = j2
If k1 = "X" Then TextBox21.Value = "n/d"
If k1 = "X" Then TextBox22.Value = "n/d"
If k1 = "X" Then TextBox21.Enabled = False
If k1 = "X" Then TextBox22.Enabled = False
If k1 = "X" Then Label13.Enabled = False
If k1 <> "X" Then TextBox21.Value = k1
If k1 <> "X" Then TextBox22.Value = k2

Label15.Caption = jest
s = ""
If jest = "" Then
Label14.Caption = "Os. Rusa " & "*****"
CommandButton1.Enabled = False
TextBox1.Value = TextBox2.Value = "*****"
TextBox1.Enabled = False
TextBox2.Enabled = False
Label1.Enabled = False
TextBox3.Value = TextBox4.Value = "*****"
TextBox3.Enabled = False
TextBox4.Enabled = False
Label4.Enabled = False
TextBox5.Value = TextBox6.Value = "*****"
TextBox5.Enabled = False
TextBox6.Enabled = False
Label5.Enabled = False
TextBox7.Value = TextBox8.Value = "*****"
TextBox7.Enabled = False
TextBox8.Enabled = False
Label6.Enabled = False
TextBox9.Value = TextBox10.Value = "*****"
TextBox9.Enabled = False
TextBox10.Enabled = False
Label7.Enabled = False
TextBox11.Value = TextBox12.Value = "*****"
TextBox11.Enabled = False
TextBox12.Enabled = False
Label8.Enabled = False
TextBox13.Value = TextBox14.Value = "*****"
TextBox13.Enabled = False
TextBox14.Enabled = False
Label9.Enabled = False
TextBox15.Value = TextBox16.Value = "*****"
TextBox15.Enabled = False
TextBox16.Enabled = False
Label10.Enabled = False
Label10.Caption = "Zastaw balkon na załamaniu OB7/8+O-33"
TextBox17.Value = TextBox18.Value = "*****"
TextBox17.Enabled = False
TextBox18.Enabled = False
Label11.Enabled = False
TextBox19.Value = TextBox20.Value = "*****"
TextBox19.Enabled = False
TextBox20.Enabled = False
Label12.Enabled = False
TextBox21.Value = TextBox22.Value = "*****"
TextBox21.Enabled = False
TextBox22.Enabled = False
Label13.Enabled = False
s = ""
i = MsgBox("Adres nie został podany", vbOKOnly, "OKNA - EWIDENCJA Błąd adresu")
Label14.Caption = "Os. Rusa " & "*****"
CommandButton1.Enabled = False
TextBox1.Value = TextBox2.Value = "*****"
TextBox1.Enabled = False
TextBox2.Enabled = False
Label1.Enabled = False
TextBox3.Value = TextBox4.Value = "*****"
TextBox3.Enabled = False
TextBox4.Enabled = False
Label4.Enabled = False
TextBox5.Value = TextBox6.Value = "*****"
TextBox5.Enabled = False
TextBox6.Enabled = False
Label5.Enabled = False
TextBox7.Value = TextBox8.Value = "*****"
TextBox7.Enabled = False
TextBox8.Enabled = False
Label6.Enabled = False
TextBox9.Value = TextBox10.Value = "*****"
TextBox9.Enabled = False
TextBox10.Enabled = False
Label7.Enabled = False
TextBox11.Value = TextBox12.Value = "*****"
TextBox11.Enabled = False
TextBox12.Enabled = False
Label8.Enabled = False
TextBox13.Value = TextBox14.Value = "*****"
TextBox13.Enabled = False
TextBox14.Enabled = False
Label9.Enabled = False
TextBox15.Value = TextBox16.Value = "*****"
TextBox15.Enabled = False
TextBox16.Enabled = False
Label10.Enabled = False
TextBox17.Value = TextBox18.Value = "*****"
TextBox17.Enabled = False
TextBox18.Enabled = False
Label11.Enabled = False
TextBox19.Value = TextBox20.Value = "*****"
TextBox19.Enabled = False
TextBox20.Enabled = False
Label12.Enabled = False
TextBox21.Value = TextBox22.Value = "*****"
TextBox21.Enabled = False
TextBox22.Enabled = False
Label13.Enabled = False
s = ""
End If

End With

Else
i = MsgBox("Do tej pory wpisanego adresu nie było na Osiedlu Rusa, ale:" & Chr(10) & "'Gdyby otworzono drzwi percepcji wszystkie rzeczy przedstawiałyby sie człowiekowi takimi, jakie sa naprawdę: jako nieskończone' /W.Blake/" & Chr(10) & "więc może spróbuj jeszcze raz :)", vbOKOnly + vbCritical, "Uważaj!")
Label15.Caption = "*****"
CommandButton1.Enabled = False
TextBox1.Value = TextBox2.Value = "*****"
TextBox1.Enabled = False
TextBox2.Enabled = False
Label1.Enabled = False
TextBox3.Value = TextBox4.Value = "*****"
TextBox3.Enabled = False
TextBox4.Enabled = False
Label4.Enabled = False
TextBox5.Value = TextBox6.Value = "*****"
TextBox5.Enabled = False
TextBox6.Enabled = False
Label5.Enabled = False
TextBox7.Value = TextBox8.Value = "*****"
TextBox7.Enabled = False
TextBox8.Enabled = False
Label6.Enabled = False
TextBox9.Value = TextBox10.Value = "*****"
TextBox9.Enabled = False
TextBox10.Enabled = False
Label7.Enabled = False
TextBox11.Value = TextBox12.Value = "*****"
TextBox11.Enabled = False
TextBox12.Enabled = False
Label8.Enabled = False
TextBox13.Value = TextBox14.Value = "*****"
TextBox13.Enabled = False
TextBox14.Enabled = False
Label9.Enabled = False
TextBox15.Value = TextBox16.Value = "*****"
TextBox15.Enabled = False
TextBox16.Enabled = False
Label10.Enabled = False
TextBox17.Value = TextBox18.Value = "*****"
TextBox17.Enabled = False
TextBox18.Enabled = False
Label11.Enabled = False
TextBox19.Value = TextBox20.Value = "*****"
TextBox19.Enabled = False
TextBox20.Enabled = False
Label12.Enabled = False
TextBox21.Value = TextBox22.Value = "*****"
TextBox21.Enabled = False
TextBox22.Enabled = False
Label13.Enabled = False
s = ""
End If

End Sub

Private Sub CommandButton3_Click()
UserForm9.Hide
End Sub

Punkt na przykładzie formularza z jednego z programów obrazujący możliwości zastosowania właściwości Enabled i ColorFont dla poprawienia funkcjonalności i czytelności formularza. Właściwość Enabled odpowiada za zablokowanie kontrolki (jest ona wtedy jasnoszara i niemożliwe jest jej użycie), a właściwość ColorFont odpowiada za kolor napisu.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wygodny formularz VBA - SetFocus KLIKNIJ by (roz)winąć

Następny przykład jest już krótszy i pochodzi z pliku 'OKNA SPISY' będącego załącznikiem do programu OKNA WYKONAWSTWO. Formularz okna spisy Prezentowany formularz ma za zadanie ułatwić wprowadzanie na bieżąco wyników pracy komisji kwalifikującej okna do wymiany. Kwalifikacja odbywa się w siedmiu różnych nieruchomościach. Komisja pracuje dwa razy w tygodniu po osiem do dziesięciu adresów dziennie. Po uruchomieniu formularz udostępnia tylko okno tekstowe gdzie należy wpisać adres w postaci 'nr wejścia/nr mieszkania'. Program sam rozpoznaje nieruchomość oraz jakie okna dostępne są na lokalu - w zależności od tej ostatniej wartości udostępnia odpowiednie okna tekstowe, a po wprowadzeniu adresu w pierwszym wolnym wierszu (lub w przypadku poprawiania danych w wierszu adresu, który już wystąpił) i właściwym dla danej nieruchomości arkuszu przygotowuje dane do sortowania adresami. Właściwości TabIndex poszczególnych formantów są tak dobrane, by klawiszem 'Enter' z klawiatury numerycznej program przechodził do kolejnych okien, na końcu do CommandButton 'Zapisz' i po jego użyciu do CommandButton 'Następny'. Tutaj następuje właściwy dla tej uwagi fragment - aby formularz był obsługiwany tylko z klawiatury numerycznej, po użyciu przycisku 'Następny' fokus musi być ustawiony w pierwszym oknie tekstowym. Uwaga dotycząca sortowania: MS Excel nie przesortuje danych o adresach w postaci nr wejścia/nr mieszkania. W swoich programach przyjąłem, że adres jest wprowadzany właśnie w tej - najbardziej chyba naturalnej postaci. Jeżeli chcę, tak jak w tym wypadku, danych sortowanych rosnąco to programowo w innej kolumnie wprowadzam zapis wwwmmm, gdzie trzy pierwsze cyfry oznaczają numer wejścia, a trzy następne numer lokalu (z ewentualnymi zerami z przodu np. dla adresu 1/12 postać zapisu do sortowania to: 001012). Zmiana formatu zapisu adresu jest prowadzona w oparciu o standardowe funkcje tekstowe MS Excel. Rozpoznanie nieruchomości również jest realizowane standardowym Excelem (mała podpowiedź - wielokrotnie zagnieżdżona funkcja 'Jeżeli'). Dla zaciętych:
Nieruchomość 1 - wejścia 11, 12, 13,
Nieruchomość 2 - wejścia od 1 do 10,
Nieruchomość 3 - wejścia od 121 - 138,
Nieruchomość 4 - wejścia od 15 do 22 i od 43 do 52,
Nieruchomość 5 - wejścia od 23 do 42,
Nieruchomość 6 - wejścia od 62 do 85,
Nieruchomość 7 - wejścia od 86 do 119.
Listingi formularza poniżej. Można jeszcze zwrócić uwagę na polecenie sformatowania daty przed zapisem do arkusza roboczego, a także sposób działania pętli wyszukującej właściwy wiersz 'Do Until Loop' - jej działanie jest przerwane w chwili, w której znajdzie wolny wiersz lub w chwili gdy znajdzie ten sam adres - to dla uniknięcia budowania osobnego formularza do poprawiania danych. W obecnym układzie można po prostu wprowadzić adres jeszcze raz. Polecenie 'Select Case' dla rozpoznania czego użytkownik chce. Uwaga ogólna: z poleceniem 'Formant.SetFocus' trzeba uważać. Próba ustawienia fokusu na elemencie niewidocznym (Visible) lub zablokowanym (Enabled) zakończy się komunikatem o błędzie. Ciekawostką w listingu jest polecenie 'UserForm1.Caption = Arkusz1.Name' ustawiające nazwę formularza w zależności od nazwy konkretnego arkusza roboczego.

FORMULARZ Kliknij, żeby (roz)winąć listing

Private Sub CommandButtonW1_Click()

Dim Arkusz_Begin As Range

If Arkusz13.Range("F11").Value = False Then
If Arkusz13.Range("E11").Value = "Nieruchomość 1" Then
Set Arkusz_Begin = Arkusz2.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 8).Value = TextBoxW5.Value 'Korytarz
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 5).Value = TextBoxW10.Value 'OB-9
Arkusz_Begin.Offset(0, 7).Value = TextBoxW11.Value 'O46
Arkusz_Begin.Offset(0, 6).Value = TextBoxW12.Value 'O51
Arkusz_Begin.Offset(0, 9).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 2" Then
Set Arkusz_Begin = Arkusz3.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 8).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 3" Then
Set Arkusz_Begin = Arkusz4.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 8).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 4" Then
Set Arkusz_Begin = Arkusz5.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW8.Value 'OB5
Arkusz_Begin.Offset(0, 8).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 9).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 5" Then
Set Arkusz_Begin = Arkusz6.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW8.Value 'OB5
Arkusz_Begin.Offset(0, 8).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 9).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 6" Then
Set Arkusz_Begin = Arkusz7.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW8.Value 'OB5
Arkusz_Begin.Offset(0, 8).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 9).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 7" Then
Set Arkusz_Begin = Arkusz8.Range("C6")
Do Until Arkusz_Begin.Value = "" Or Arkusz_Begin.Offset(0, 1) = TextBoxW1.Value
Set Arkusz_Begin = Arkusz_Begin.Offset(1, 0)
Loop

Arkusz_Begin.Value = Format(TextBoxW3.Value, "yyyy-mm-dd") 'Data
Arkusz_Begin.Offset(0, 1).Value = TextBoxW1.Value 'adres
Arkusz_Begin.Offset(0, 2).Value = TextBoxW2.Value 'protokół
Arkusz_Begin.Offset(0, 3).Value = TextBoxW4.Value 'O-17
Arkusz_Begin.Offset(0, 5).Value = TextBoxW5.Value 'O-32
Arkusz_Begin.Offset(0, 4).Value = TextBoxW7.Value 'O-38
Arkusz_Begin.Offset(0, 6).Value = TextBoxW6.Value 'O-34
Arkusz_Begin.Offset(0, 7).Value = TextBoxW8.Value 'OB5
Arkusz_Begin.Offset(0, 8).Value = TextBoxW9.Value 'OB7
Arkusz_Begin.Offset(0, 9).Value = TextBoxW13.Value 'Uwagi
Arkusz_Begin.Offset(0, 12).Value = CStr(Arkusz13.Range("J5").Value)
CommandButtonW1.Enabled = False
CommandButtonW2.SetFocus
End If
End If

End Sub

Private Sub CommandButtonW2_Click()
FrameW1.Enabled = True
LabelW1.Enabled = True
LabelW2.Enabled = False
LabelW3.Enabled = False
LabelW4.Enabled = False
LabelW5.Enabled = False
LabelW6.Enabled = False
LabelW7.Enabled = False
LabelW8.Enabled = False
LabelW9.Enabled = False
LabelW10.Enabled = False
LabelW11.Enabled = False
LabelW12.Enabled = False
LabelW13.Enabled = False
TextBoxW1.Value = TextBoxW2.Value =TextBoxW3.Value = TextBoxW4.Value = TextBoxW5.Value = TextBoxW6.Value = TextBoxW7.Value = TextBoxW8.Value = TextBoxW9.Value = TextBoxW10.Value = TextBoxW11.Value = TextBoxW12.Value = TextBoxW13.Value = ""
TextBoxW2.Enabled = False
TextBoxW3.Enabled = False
TextBoxW4.Enabled = False
TextBoxW5.Enabled = False
TextBoxW6.Enabled = False
TextBoxW7.Enabled = False
TextBoxW8.Enabled = False
TextBoxW9.Enabled = False
TextBoxW10.Enabled = False
TextBoxW11.Enabled = False
TextBoxW12.Enabled = False
TextBoxW13.Enabled = False
CommandButtonW1.Enabled = False

TextBoxW1.SetFocus
End Sub
Private Sub TextBoxW1_Exit(ByVal Cancel As MSForms.ReturnBoolean)

Dim i As Integer 'Deklaracja zmiennych

Arkusz13.Range("C5").Value = TextBoxW1.Value

If TextBoxW1.Value <> "" Then 'Zabezpieczenie przed nadmiernymi komunikatami

If Arkusz13.Range("F11").Value = True Then
GoTo Brak_nieruchomości:
Else
Call Szukanie_adresu

End If

End If 'Koniec zabezpieczenia

Exit Sub

Brak_nieruchomości:
i = MsgBox("Nieruchomość nie została rozpoznana" & Chr(10) & "Wprowadź adres w postaci:" & Chr(10) & "nr wejścia / numer lub opis lokalu", vbOKOnly + vbCritical, Arkusz1.Name & " Uważaj!")

FrameW1.Enabled = True
LabelW1.Enabled = True
LabelW2.Enabled = False
LabelW3.Enabled = False
LabelW4.Enabled = False
LabelW5.Caption = "O32/O33 szt."
LabelW5.Enabled = False
LabelW6.Enabled = False
LabelW7.Enabled = False
LabelW8.Enabled = False
LabelW9.Enabled = False
LabelW10.Enabled = False
LabelW11.Enabled = False
LabelW12.Enabled = False
LabelW13.Enabled = False
TextBoxW1.Value = TextBoxW2.Value = TextBoxW3.Value = TextBoxW4.Value = TextBoxW5.Value = TextBoxW6.Value = TextBoxW7.Value = TextBoxW8.Value = TextBoxW9.Value = TextBoxW10.Value = TextBoxW11.Value = TextBoxW12.Value = TextBoxW13.Value = ""
TextBoxW2.Enabled = False
TextBoxW3.Enabled = False
TextBoxW4.Enabled = False
TextBoxW5.Enabled = False
TextBoxW6.Enabled = False
TextBoxW7.Enabled = False
TextBoxW8.Enabled = False
TextBoxW9.Enabled = False
TextBoxW10.Enabled = False
TextBoxW11.Enabled = False
TextBoxW12.Enabled = False
TextBoxW13.Enabled = False
CommandButtonW1.Enabled = False

End Sub

Private Sub UserForm_Initialize()

UserForm1.Caption = Arkusz1.Name

Arkusz13.Range("I1").Value = "Normal"
'Wartości początkowe - ramka wprowadzania danych
FrameW1.Enabled = True
LabelW1.Enabled = True
LabelW2.Enabled = False
LabelW3.Enabled = False
LabelW4.Enabled = False
LabelW5.Caption = "O32/O33 szt."
LabelW5.Enabled = False
LabelW6.Enabled = False
LabelW7.Enabled = False
LabelW8.Enabled = False
LabelW9.Enabled = False
LabelW10.Enabled = False
LabelW11.Enabled = False
LabelW12.Enabled = False
LabelW13.Enabled = False
TextBoxW1.Value = TextBoxW2.Value = TextBoxW3.Value = TextBoxW4.Value = TextBoxW5.Value = TextBoxW6.Value = TextBoxW7.Value = TextBoxW8.Value = TextBoxW9.Value = TextBoxW10.Value = TextBoxW11.Value = TextBoxW12.Value = TextBoxW13.Value = ""
TextBoxW2.Enabled = False
TextBoxW3.Enabled = False
TextBoxW4.Enabled = False
TextBoxW5.Enabled = False
TextBoxW6.Enabled = False
TextBoxW7.Enabled = False
TextBoxW8.Enabled = False
TextBoxW9.Enabled = False
TextBoxW10.Enabled = False
TextBoxW11.Enabled = False
TextBoxW12.Enabled = False
TextBoxW13.Enabled = False
CommandButtonW1.Enabled = False
'CommandButtonW2.Enabled = False

End Sub

PODPROCEDURA z modułu zewnętrznego Kliknij, żeby (roz)winąć listing

Sub Szukanie_adresu()

Dim i As Integer
Dim s As String
Dim jest As Range

On Error GoTo Obsługa_błędu:

If Arkusz13.Range("F11").Value = False Then
If Arkusz13.Range("E11").Value = "Nieruchomość 1" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O-17+O-3 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = False
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = False
UserForm1.LabelW9.Enabled = False
UserForm1.LabelW10.Enabled = True
UserForm1.LabelW11.Enabled = True
UserForm1.LabelW12.Enabled = True
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = False
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = False
UserForm1.TextBoxW9.Enabled = False
UserForm1.TextBoxW10.Enabled = True
UserForm1.TextBoxW11.Enabled = True
UserForm1.TextBoxW12.Enabled = True
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 2" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = False
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = False
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 3" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = False
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = False
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 4" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = True
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = True
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 5" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = True
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = True
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 6" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = True
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = True
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
ElseIf Arkusz13.Range("E11").Value = "Nieruchomość 7" Then
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = True
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = True
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.TextBoxW12.Enabled = False
UserForm1.TextBoxW13.Enabled = True
End If
End If

Arkusz13.Range("C39:C49").ClearContents 'Wyczyszczenie znaczników adresów

s = Arkusz13.Range("C5").Value
'Program coś pieprzy gdy s z zakresu od 1-6 /prawdopodobnie CurrentRegion/
'
If s = "1" Or s = "2" Or s = "3" Or s = "4" Or s = "5" Or s = "6" Or s = "7" Then s = ""
'
Set jest = Arkusz12.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
If Not jest Is Nothing Then
With jest
If jest.Offset(0, 3).Value = "X" Then Arkusz13.Range("C39").Value = "X" 'O-17
If jest.Offset(0, 5).Value = "X" Then Arkusz13.Range("C40").Value = "X" 'O-33
If jest.Offset(0, 7).Value = "X" Then Arkusz13.Range("C41").Value = "X" 'O-17d + O-3
If jest.Offset(0, 9).Value = "X" Then Arkusz13.Range("C42").Value = "X" 'O-39 wschód
If jest.Offset(0, 11).Value = "X" Then Arkusz13.Range("C43").Value = "X" 'O-39 zachód
If jest.Offset(0, 13).Value = "X" Then Arkusz13.Range("C44").Value = "X" 'O-39 kuchnia
If jest.Offset(0, 15).Value = "X" Then Arkusz13.Range("C45").Value = "X" 'OB5/6 + O34
If jest.Offset(0, 17).Value = "X" Then Arkusz13.Range("C46").Value = "X" 'OB7/8 + O-33
If jest.Offset(0, 19).Value = "X" Then Arkusz13.Range("C47").Value = "X" 'OB7/8 + O-34
If jest.Offset(0, 21).Value = "X" Then Arkusz13.Range("C48").Value = "X" 'OB7/8 + O-39
If jest.Offset(0, 23).Value = "X" Then Arkusz13.Range("C49").Value = "X" 'OB9/10+O47+O51

If s = "126/4" Or s = "126/8" Or s = "126/12" Or s = "126/16" Or s = "126/20" Or s = "126/24" Or s = "126/28" Or s = "126/32" Or s = "126/36" Or s = "126/40" Or s = "126/45" Then
Arkusz13.Range("C46").Value = ""
End If

End With

UserForm1.LabelW2.Enabled = True
UserForm1.LabelW3.Enabled = True
UserForm1.LabelW4.Enabled = True
UserForm1.LabelW5.Caption = "O32/O33 szt."
UserForm1.LabelW5.Enabled = True
UserForm1.LabelW6.Enabled = True
UserForm1.LabelW7.Enabled = True
UserForm1.LabelW8.Enabled = True
UserForm1.LabelW9.Enabled = True
UserForm1.LabelW10.Enabled = True
UserForm1.LabelW11.Enabled = True
UserForm1.LabelW12.Enabled = True
UserForm1.LabelW13.Enabled = True
UserForm1.TextBoxW2.Enabled = True
UserForm1.TextBoxW3.Enabled = True
UserForm1.TextBoxW4.Enabled = True
UserForm1.TextBoxW5.Enabled = True
UserForm1.TextBoxW6.Enabled = True
UserForm1.TextBoxW7.Enabled = True
UserForm1.TextBoxW8.Enabled = True
UserForm1.TextBoxW9.Enabled = True
UserForm1.TextBoxW10.Enabled = True
UserForm1.TextBoxW11.Enabled = True
UserForm1.TextBoxW12.Enabled = True
UserForm1.TextBoxW13.Enabled = True

If Arkusz13.Range("E11").Value = "Nieruchomość 1" Then
UserForm1.LabelW5.Caption = "O-17+O-3 szt."
If Arkusz13.Range("C41").Value = "X" Then
UserForm1.LabelW5.Enabled = False
UserForm1.TextBoxW5.Enabled = False
Else
UserForm1.LabelW5.Enabled = True
UserForm1.TextBoxW5.Enabled = True
End If
'UserForm1.LabelW4.Enabled = False
'UserForm1.TextBoxW4.Enabled = False
End If

If Arkusz13.Range("C39").Value = "X" Then 'O17
UserForm1.LabelW4.Enabled = False
UserForm1.TextBoxW4.Enabled = False
End If

If Arkusz13.Range("C40").Value = "X" And Arkusz13.Range("E11").Value <> "Nieruchomość 1" Then 'O33
UserForm1.LabelW5.Enabled = False
UserForm1.TextBoxW5.Enabled = False
End If

If Arkusz13.Range("C45").Value = "X" And Arkusz13.Range("C47").Value = "X" Then 'O34
UserForm1.LabelW6.Enabled = False
UserForm1.TextBoxW6.Enabled = False
End If

If Arkusz13.Range("C42").Value = "X" And Arkusz13.Range("C43").Value = "X" And Arkusz13.Range("C44").Value = "X" And Arkusz13.Range("C48").Value = "X" Then 'O39
UserForm1.LabelW6.Enabled = False
UserForm1.TextBoxW6.Enabled = False
End If

If Arkusz13.Range("C45").Value = "X" Then 'OB5/6
UserForm1.LabelW8.Enabled = False
UserForm1.TextBoxW8.Enabled = False
End If

If Arkusz13.Range("C46").Value = "X" And Arkusz13.Range("C47").Value = "X" And Arkusz13.Range("C48").Value = "X" Then 'OB7/8
UserForm1.LabelW9.Enabled = False
UserForm1.TextBoxW9.Enabled = False
End If

If Arkusz13.Range("C49").Value = "X" Then 'OB9/10+O47+O51
UserForm1.LabelW10.Enabled = False
UserForm1.TextBoxW10.Enabled = False
UserForm1.LabelW11.Enabled = False
UserForm1.TextBoxW11.Enabled = False
UserForm1.LabelW12.Enabled = False
UserForm1.TextBoxW12.Enabled = False
End If

UserForm1.TextBoxW2.SetFocus
UserForm1.CommandButtonW1.Enabled = True

Else
i = MsgBox("Do tej pory adresu: " + s + " nie było na Osiedlu Rusa, ale:" & Chr(10) & "'Gdyby otworzono drzwi percepcji wszystkie rzeczy przedstawiałyby się" & "człowiekowi takimi, jakie sa naprawdę: jako nieskończone' /W.Blake/" & Chr(10) & "Czy chcesz kontynuować mimo wszystko ?", vbInformation + vbYesNo, "Uważaj!")

Select Case i
Case 6
GoTo Zapis_danych:
End Select

End If

Exit Sub

Zapis_danych:
UserForm1.LabelW2.Enabled = True
UserForm1.LabelW3.Enabled = True
UserForm1.TextBoxW2.Enabled = True
UserForm1.TextBoxW3.Enabled = True

UserForm1.CommandButtonW1.Enabled = True
UserForm1.TextBoxW2.SetFocus

Exit Sub
Obsługa_błędu:
UserForm1.Hide

End Sub

Właściwość SetFocus czyni daną kontrolkę aktywną np. przenosi kursor do okna tekstowego, podświetla plik itp. Focus można ustawiać z poziomu edytora dla zadanego formularza, ale można również programować ją w trakcie wykonywania algorytmu. O tej drugiej metodzie mówi niniejszy punkt, opierający się na prostym, rzeczywiście stosowanym przeze mnie formularzu.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Definicja funkcji własnej Excela

Przykład pochodzi z pliku LISTA OBECNOŚCI, która stanowi nieco zmodyfikowany wzór wyjaśniony w książce pana Sergiusza Flanczewskiego 'Excel z elementami VBA w firmie'. Sam algorytm wyznaczania daty świąt wielkanocnych dla danego roku, jak podaje w/w pozycja, opracował Karl Friedrich Gauss (1777-1855) - no cóż chciałoby sie rzec 'stare, ale jare'. Funkcja zdefiniowana została w elemencie 'Modules/Module1' wymienionego pliku. Później można wywoływać ją jak każdą inną funkcję Excela przez '=nazwa_funkcji(argumenty)' w komórkach arkuszy roboczych.

Def. FUNKCJI WŁASNEJ Kliknij, żeby (roz)winąć listing

Function Wielkanoc(rok)

If rok ≤ 1583 Then
G1 = 15
H1 = 6
End If
If rok ≥= 1583 And rok ≤= 1699 Then
G1 = 22
H1 = 2
End If
If rok ≥ 1699 And rok ≤= 1799 Then
G1 = 23
H1 = 3
End If
If rok ≥ 1799 And rok ≤= 1899 Then
G1 = 23
H1 = 4
End If
If rok ≥ 1899 And rok ≤= 2099 Then
G1 = 24
H1 = 5
End If
If rok ≥ 2099 And rok ≤= 2199 Then
G1 = 24
H1 = 6
End If

a1 = rok Mod 19
b1 = rok Mod 4
c1 = rok Mod 7
d1 = (19 * a1 + G1) Mod 30
e1 = (2 * b1 + 4 * c1 + 6 * d1 + H1) Mod 7
d4 = 22 + d1 + e1
d5 = d4 Mod 31
d6 = d5
If d5 ≥ 25 Then
d6 = d5 - 7
End If

Wielkanoc = DateSerial(rok, 4, d6)
If d4 ≤= 31 Then
Wielkanoc = DateSerial(rok, 3, d4)
End If

End Function

Działanie pliku polega w skrócie na formatowaniu warunkowym komórek listy obecności. W komórkach znajdują się liczby zależne od dnia tygodnia/święta. Program trafia na liczbę odpowiednio zdefiniowaną i zaznacza obszar kolorem. W niektórych drukarkach trzeba wyłączyć funkcję 'Drukuj czcionkę na czarno'.

Polecam również artykuły Dni tygodnia i data wielkanocy oraz Funkcja własna w Excelu - argumenty, opis, przynależność w dziale 'Zagadnienia VBA'.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wyszukiwanie w zakresie komórek

Niemal wszystkie prezentowane na stronie programy w excelu opierają się na wyszukiwaniu w kolumnie adresu lokalu. W powyższych listingach wystąpiły już tego typu pętle (np. 'Obliczenia wartości księgowych w Excelu. Dlaczego suma złotówek się nie zgadza', 'Wygodny formularz. Właściwość Visible', 'Wygodny formularz. Właściwość ColorFont i Enabled', 'Wygodny formularz. Właściwość Setfocus'), wobec czego nie będę przytaczał ich w całości, a tylko ramowy układ:

PRZYKŁAD SZUKANIA Kliknij, żeby (roz)winąć listing

Sub Przykład_szukania

Dim i As Integer
Dim s As String
Dim jest As Range

s = InputBox("Podaj adres do wyszukania", "Szukanie lokalu", "1/2")
Zmienną s do szukania można wczytać z okna InputBox, etykiety (s = LabelXX.Caption) lub z okna tekstowego (s = TextBoxYY.Value) formularza
If s = "1" Or s = "2" Or s = "3" Or s = "4" Or s = "5" Or s = "6" Or s = "7" Then s = ""
Należy zabezpieczyć się przed dziwnymi wartościami, pamiętając, że 'Cancel' w oknie InputBox zwraca łańcuch tekstowy zerowej długości.
Set jest = Arkusz3.Range("B7").CurrentRegion.Find(s, LookAt:=xlWhole)
Wskazujemy początek szukania - o jedną komórkę nad pierwszą, w której występuje adres

If Not jest Is Nothing Then
With jest
Program znalazł szukaną wartość i tutaj wprowadzamy, co ma zrobić. W moich programach w tym miejscu następuje cały szereg podstawienia pod zdefiniowane zmienne wartości komórek na prawo od adresu.

End With

Koniec szukania - tutaj programy w excelu wstawiają wartości do etykiet formularza itp.

Else
i = MsgBox("Do tej pory adresu: " + s + " nie było na Osiedlu Rusa, ale:" & Chr(10) & "'Gdyby otworzono drzwi percepcji wszystkie rzeczy przedstawiałyby sie człowiekowi takimi, jakie sa naprawdę: jako nieskończone' /W.Blake/" & Chr(10) & "więc może spróbuj jeszcze raz :)", vbOKOnly + vbCritical, "Uważaj!")

Koniec szukania, ale program nie znalazł wartości w spisie. Tutaj następuje określenie co ma zrobić.
End If

Wyjście z algorytmu szukania.

End Sub

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Konwersja typu zmiennej z TextBox - przecinek i kropka w VBA KLIKNIJ by (roz)winąć

Kropka zamiast przecinka na stałe w Excelu Excel standardowo korzysta z separatorów systemowych tzn. używa kropki lub przecinka tak jak jest to przyjęte w wersji językowej programu. Można opcje tą oczywiście zmienić - w Excelu 2003 menu 'Narzędzia/Opcje/Międzynarodowe' i ustawić inny separator zapisu liczb niż systemowy. Edytor VBA takiej opcji nie posiada (lub ja jej po prostu nie znalazłem). W efekcie wartości liczbowe obliczone wewnątrz procedury, pobrane z komórek itp. i wstawione do kontrolek np. okien tekstowych zawierają zamiast przecinka znak kropki. Przy próbie bezpośredniego wstawienia tego typu wartości do komórki arkusza może być ona potraktowana przez program jak tekst, a nie liczba.
Uwaga ta wystąpiła już wcześniej w punkcie 'Obliczenia wartości księgowych w Excelu. Dlaczego suma złotówek się nie zgadza', w którym był przytoczony przykład z programu WYKUP GRUNTÓW. Proszę zwrócić uwagę, że wartość w oknie tekstowym 'Powierzchnia lokalu' prezentowanego, w w/w uwadze, formularza jest wartością liczbową wczytaną z arkusza roboczego MS Excel. VBA wstawiając ją do okna tekstowego wstawił ją z takim samym znakiem wartości dziesiętnych jak w arkuszu tj. przecinka. Jeżeli teraz bezpośrednio pobierzemy tę wartość do obliczeń ten sam VBA wyrzuci komunikat o błędzie ponieważ nie rozpozna wartości TextBox-a jako liczby. Jeżeli musimy pobrać wartość z okna tekstowego do obliczeń to należy zamienić przecinek na kropkę. Ze względu na fakt, że prezentowany program nie ja miałem użytkować tłumaczenie zagadnienia 'kropka, a przecinek' byłoby niewskazane, a samo zastosowanie uciążliwe. Dużo łatwiej było konwertować wartość wpisaną w oknie tekstowym na wartość liczbową przed wykonaniem obliczeń - patrz polecenie CCur(zmienna) podane niżej:

Powierzchnia = CCur(Format(jest.Offset(0, 2).Value, "0.#0"))

Teraz mamy sytuację odwrotną - jeżeli w omawianym oknie zamienimy przecinek na kropkę, to VBA zrewanżuje się komunikatem o braku możliwości konwertowania wartości na wartość liczbową. Ze względu na fakt, że wartości liczbowe wprowadzane z klawiatury numerycznej mają znak przecinka, przyjąłem po prostu, że 'ten typ tak ma', a program wykonuje zadanie, dla którego powstał ;).

Jeśli jesteś zainteresowany tematem uzyskania kwot słownie w excelu i makrem do ściągnięcia to oprócz tematów w tym dziale polecam artykuł z działu ZAGADNIENIA VBA - Kwota słownie w Excelu.

UWAGA:
Przygotowałem samodzielną wtyczkę rozszerzającą możliwości Excela między innymi o zapis słowny kwoty - opis na stronie Dodatek_GK. Zastosowanie wtyczki nie wymaga umiejętności programowania.

Punkt przedstawia zagadnienia zmiany w Excelu tzw separatorów systemowych czyli własnie kropki i przecinka w dzisiętnym zapisie liczb oraz możliwości konwersji i formatowania danych wprowadzanych do kontrolki TextBox. Jak wszystkie artykuły tej podstrony powstał na podstawie rzeczywistych, zastosowanych przeze mnie rozwiązaniach. Inne zbliżone tematycznie punkty z tego zakresu przedstawiam w dziale "Zagadnienia VBA" np. Blokowanie wprowadzania liter do TextBox.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Instrukcje pętli i warunkowe - przykład zastosowania w VBA

Przykład pochodzi z programu OKNA KORESPONDENCJA. Jest to fragment algorytmu związany z wydrukiem potwierdzenia przyjęcia wniosku o wypłatę ekwiwalentu bez kwalifikacji. Formularz ekwiwalent bez kwalifikacjiPo wprowadzeniu adresu w oknie tekstowym i użyciu przycisku 'aktualizuj treść pisma' program powinien kolejno:
- sprawdzić do arkusza której nieruchomości przypisać adres,
- sprawdzić, czy adres już nie występuje i czy został zrealizowany (dopóki pierwszy wniosek z lokalu nie zostanie zrealizowany każdy następny uznaje się za uzupełnienie wniosku). Jeżeli adres został zrealizowany program na pierwszym wolnym miejscu zapisze kolejny wniosek, informując użytkownika, że jest to kolejny wniosek i podając datę realizacji poprzedniego. Jeżeli adres nie został jeszcze zrealizowany program odmówi rejestracji nowego wniosku podając datę złożenia poprzedniego,
- przy rejestrowaniu wniosku program poda i wstawi do druku pisma numer kolejny wniosku, datę złożenia, ilość dotychczas zrealizowanycych w nieruchomości wniosków oraz poprosi o podanie ewentualnych uwag do istniejącego wniosku,
Założenia są może nieco skomplikowane, ale będą bardziej przejrzyste po zapoznaniu się z instrukcją programu. Jest to jak sądzę ciekawa kombinacja pętli warunkowej 'If Then Else', oraz pętli o znanych i nieznanych ilościach powtórzeń. Poniżej przedstawiam listing dla jednej nieruchomości. W oryginalnym kodzie programu powtórzony jest on siedem razy.

PĘTLE i WARUNKI Kliknij, żeby (roz)winąć listing

Private Sub CommandButtonP3_Click()

Dim Szuk_wniosek As Range
Dim i As Integer, a As Integer
Dim Uwaga_1 As String
Dim L_wniosków_w_nier As Single
Dim Realizacja_wniosków As Range
Dim L_wniosków_na_adresie As Single

If Arkusz2.Range("H14").Value = "N1 (OR 11,12,13)" Then 'POCZĄTEK NIERUCHOMOŚCI NR 1

L_wniosków_w_nier = 0 'Określenie liczby wniosków zrealizowanych
Set Realizacja_wniosków = Arkusz25.Range("E3")
For i = 0 To 3100
If Realizacja_wniosków.Value <> "" Then
L_wniosków_w_nier = L_wniosków_w_nier + 1
End If
Set Realizacja_wniosków = Realizacja_wniosków.Offset(1, 0)
Next
Label4_n.Caption = Arkusz16.Range("A11").Value
Label4_n.Enabled = True
Label5_n.Enabled = True
Arkusz2.Range("H17").Value = L_wniosków_w_nier

If Arkusz2.Range("J17").Value = 2 Or Arkusz2.Range("J17").Value = 3 Or Arkusz2.Range("J17").Value = 4 Then
Label5_na.Caption = L_wniosków_w_nier
Label5_nb.Caption = "wnioski"
Label5_na.Enabled = True
Label5_nb.Enabled = True
Else
Label5_na.Caption = L_wniosków_w_nier
Label5_nb.Caption = "wniosków"
Label5_na.Enabled = True
Label5_nb.Enabled = True
End If

If Arkusz2.Range("H17").Value = 1 Then
Label5_na.Caption = L_wniosków_w_nier
Label5_nb.Caption = "wniosek"
Label5_na.Enabled = True
Label5_nb.Enabled = True
End If
L_wniosków_na_adresie = 1 'Określenie liczby wniosków z podanego adresu
Set Szuk_wniosek = Arkusz25.Range("C2")
For i = 0 To 3100
If Szuk_wniosek.Value = TextBox_adres Then
L_wniosków_na_adresie = L_wniosków_na_adresie + 1
End If
Set Szuk_wniosek = Szuk_wniosek.Offset(1, 0)
Next
Set Szuk_wniosek = Arkusz25.Range("C2") 'Ustawienie początku wyszukiwania

L_wniosków_na_adresie = L_wniosków_na_adresie - 1
a = 1
For a = 1 To L_wniosków_na_adresie 'Pętla wykonywana tyle razy ile jest wniosków lub przynajmniej raz
Do
Set Szuk_wniosek = Szuk_wniosek.Offset(1, 0)
If Szuk_wniosek.Value = TextBox_adres.Value Then
i = MsgBox("Wniosek nr " & a & " lokalu " & TextBox_adres.Value & " został już wcześniej zarejestrowany" & Chr(10) & "z datą: " & Szuk_wniosek.Offset(0, 1).Value, 0, "OKNA KORESPONDENCJA")
If Szuk_wniosek.Offset(0, 2).Value = "" Then
i = MsgBox("Realizacja wniosku nr " & a & " jeszcze nie nastąpiła.", 0, "OKNA KORESPONDENCJA")
Else
i = MsgBox("Realizacja wniosku nr " & a & " została zarejestrowana" & Chr(10) & "z datą: " & Szuk_wniosek.Offset(0, 2).Value, 0, "OKNA KORESPONDENCJA")
End If
End If
Loop Until Szuk_wniosek.Value = TextBox_adres.Value Or Szuk_wniosek.Value = ""
Next
If Szuk_wniosek.Value = TextBox_adres.Value And Szuk_wniosek.Offset(0, 2).Value <> "" Then
Do
Set Szuk_wniosek = Szuk_wniosek.Offset(1, 0)
Loop Until Szuk_wniosek.Value = ""
End If

If Szuk_wniosek.Value = "Adres" Then
Do
Set Szuk_wniosek = Szuk_wniosek.Offset(1, 0)
Loop Until Szuk_wniosek.Value = ""
End If

If Szuk_wniosek.Value = "" Then
Szuk_wniosek.Value = TextBox_adres.Value
Szuk_wniosek.Offset(0, 1).Value = TextBox_data.Value
TextBox_adres.Enabled = False
i = MsgBox("Wniosek nr " & a & " lokalu " & TextBox_adres.Value & " został właśnie zarejestrowany" & Chr(10) & "z datą: " & Szuk_wniosek.Offset(0, 1).Value, 0, "OKNA KORESPONDENCJA")
Uwaga_1 = InputBox("Podaj ewentualne uwagi do wniosku", "OKNA KORESPONDENCJA Uwagi do wniosku", "")
Szuk_wniosek.Offset(0, 3).Value = Uwaga_1
Label3_na.Caption = Szuk_wniosek.Offset(0, -1).Value
Label3_na.Enabled = True
CheckBox8_f.Enabled = True
CheckBox8_o.Enabled = True
CheckBox8_all.Enabled = True
Label6_n.Enabled = True
End If

End If 'Koniec Arkusza Nieruchomości 1

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Kwota słownie w Excelu - algorytm prosty, a długi

Algorytm sprawdzający podaną kwotę i wpisujący ją słownie. Jest to część pracy za którą nigdy nie przepadałem - czyli jedna z pierwszych, które postanowiłem zaprogramować. Nie chciałem, by procedura działała tak jak niektóre programy bankowe, które kwotę słownie wprowadzają jako nazwy pojedynczych cyfr oddzielonych symbolem gwiazdki. Kwota 16854,20 powinna być zapisana jako 'szesnaście tysięcy osiemset pięćdziesiąt cztery złote 20/100 groszy', a nie 'jeden *sześć *osiem *pięć *cztery *złote *dwa *zero *groszy'. Nie znałem jednak zmiennych tablicowych i powstał informatyczny potworek - tyle że sprawnie wykonujący swoje zadanie. Kwotę wstawiłem do odpowiedniej komórki i formułami matematycznymi określam w niej ilość tysięcy, setek, dziesiątek itd. Z powyższych powodów proszę się nie śmiać i potraktować przykład jako edukacyjny - proszę porównać poniższy kod z algorytmem podanym w uwadze 'Kwota słownie - algorytm krótszy, a trudniejszy'. Obydwa robią to samo, ale jest różnica klas ;).
UWAGA - Polecam również temat na blogu p. Marcina Egerta - tutaj.

KWOTA SŁOWNIE w EXCELU - długi Kliknij, żeby (roz)winąć listing

Sub Słownie()
Dim Słownie As String

'Tysiące
If Arkusz33.Range("D8") = "0" Then Słownie = " "
If Arkusz33.Range("D8") = "1" Then Słownie = "jeden tysiąc"
If Arkusz33.Range("D8") = "2" Then Słownie = "dwa tysiące"
If Arkusz33.Range("D8") = "3" Then Słownie = "trzy tysiące"
If Arkusz33.Range("D8") = "4" Then Słownie = "cztery tysiące"
If Arkusz33.Range("D8") = "5" Then Słownie = "pięć tysięcy"
If Arkusz33.Range("D8") = "6" Then Słownie = "sześć tysięcy"
If Arkusz33.Range("D8") = "7" Then Słownie = "siedem tysięcy"
If Arkusz33.Range("D8") = "8" Then Słownie = "osiem tysięcy"
If Arkusz33.Range("D8") = "9" Then Słownie = "dziewięć tysięcy"
If Arkusz33.Range("D8") = "10" Then Słownie = "dziesięć tysięcy"
If Arkusz33.Range("D8") = "11" Then Słownie = "jedenaście tysięcy"
If Arkusz33.Range("D8") = "12" Then Słownie = "dwanaście tysięcy"
If Arkusz33.Range("D8") = "13" Then Słownie = "trzynaście tysięcy"
If Arkusz33.Range("D8") = "14" Then Słownie = "czternaście tysięcy"
If Arkusz33.Range("D8") = "15" Then Słownie = "piętnaście tysięcy"
If Arkusz33.Range("D8") = "16" Then Słownie = "szesnaście tysięcy"
If Arkusz33.Range("D8") = "17" Then Słownie = "siemdemnaście tysięcy"
If Arkusz33.Range("D8") = "18" Then Słownie = "osiemnaście tysięcy"
If Arkusz33.Range("D8") = "19" Then Słownie = "dziewiętnaście tysięcy"
If Arkusz33.Range("D8") = "20" Then Słownie = "dwadzieścia tysięcy"
If Arkusz33.Range("D8") = "21" Then Słownie = "dwadzieścia jeden tysięcy"
If Arkusz33.Range("D8") = "22" Then Słownie = "dwadzieścia dwa tysięce"
If Arkusz33.Range("D8") = "23" Then Słownie = "dwadzieścia trzy tysięce"
If Arkusz33.Range("D8") = "24" Then Słownie = "dwadzieścia cztery tysięce"
If Arkusz33.Range("D8") = "25" Then Słownie = "dwadzieścia pięć tysięcy"
If Arkusz33.Range("D8") = "26" Then Słownie = "dwadzieścia sześć tysięcy"
If Arkusz33.Range("D8") = "27" Then Słownie = "dwadzieścia siedem tysięcy"
If Arkusz33.Range("D8") = "28" Then Słownie = "dwadzieścia osiem tysięcy"
If Arkusz33.Range("D8") = "29" Then Słownie = "dwadzieścia dziewięć tysięcy"
If Arkusz33.Range("D8") = "30" Then Słownie = "trzydzieści tysięcy"
If Arkusz33.Range("D8") = "31" Then Słownie = "trzydzieści jeden tysięcy"
If Arkusz33.Range("D8") = "32" Then Słownie = "trzydzieści dwa tysięcy"
If Arkusz33.Range("D8") = "33" Then Słownie = "trzydzieści trzy tysięce"
If Arkusz33.Range("D8") = "34" Then Słownie = "trzydzieści cztery tysięce"
If Arkusz33.Range("D8") = "35" Then Słownie = "trzydzieści pięć tysięcy"
If Arkusz33.Range("D8") = "36" Then Słownie = "trzydzieści sześć tysięcy"
If Arkusz33.Range("D8") = "37" Then Słownie = "trzydzieści siedem tysięcy"
If Arkusz33.Range("D8") = "38" Then Słownie = "trzydzieści osiem tysięcy"
If Arkusz33.Range("D8") = "39" Then Słownie = "trzydzieści dziewięć tysięcy"
If Arkusz33.Range("D8") = "40" Then Słownie = "czterdzieści tysięcy"
If Arkusz33.Range("D8") = "41" Then Słownie = "czterdzieści jeden tysięcy"
If Arkusz33.Range("D8") = "42" Then Słownie = "czterdzieści dwa tysięce"
If Arkusz33.Range("D8") = "43" Then Słownie = "czterdzieści trzy tysięce"
If Arkusz33.Range("D8") = "44" Then Słownie = "czterdzieści cztery tysięce"
If Arkusz33.Range("D8") = "45" Then Słownie = "czterdzieści pięć tysięcy"
If Arkusz33.Range("D8") = "46" Then Słownie = "czterdzieści sześć tysięcy"
If Arkusz33.Range("D8") = "47" Then Słownie = "czterdzieści siedem tysięcy"
If Arkusz33.Range("D8") = "48" Then Słownie = "czterdzieści osiem tysięcy"
If Arkusz33.Range("D8") = "49" Then Słownie = "czterdzieści dziewięć tysięcy"
If Arkusz33.Range("D8") = "50" Then Słownie = "pięćdziesiąt tysięcy"
If Arkusz33.Range("D8") = "51" Then Słownie = "pięćdziesiąt jeden tysięcy"
If Arkusz33.Range("D8") = "52" Then Słownie = "pięćdziesiąt dwa tysięce"
If Arkusz33.Range("D8") = "53" Then Słownie = "pięćdziesiąt trzy tysięce"
If Arkusz33.Range("D8") = "54" Then Słownie = "pięćdziesiąt cztery tysięce"
If Arkusz33.Range("D8") = "55" Then Słownie = "pięćdziesiąt pięć tysięcy"
If Arkusz33.Range("D8") = "56" Then Słownie = "pięćdziesiąt sześć tysięcy"
If Arkusz33.Range("D8") = "57" Then Słownie = "pięćdziesiąt siedem tysięcy"
If Arkusz33.Range("D8") = "58" Then Słownie = "pięćdziesiąt osiem tysięcy"
If Arkusz33.Range("D8") = "59" Then Słownie = "pięćdziesiąt dziewięć tysięcy"
If Arkusz33.Range("D8") = "60" Then Słownie = "sześćdziesiąt tysięcy"
If Arkusz33.Range("D8") = "61" Then Słownie = "sześćdziesiąt jeden tysięcy"
If Arkusz33.Range("D8") = "62" Then Słownie = "sześćdziesiąt dwa tysięce"
If Arkusz33.Range("D8") = "63" Then Słownie = "sześćdziesiąt trzy tysięce"
If Arkusz33.Range("D8") = "64" Then Słownie = "sześćdziesiąt cztery tysięce"
If Arkusz33.Range("D8") = "65" Then Słownie = "sześćdziesiąt pięć tysięcy"
If Arkusz33.Range("D8") = "66" Then Słownie = "sześćdziesiąt sześć tysięcy"
If Arkusz33.Range("D8") = "67" Then Słownie = "sześćdziesiąt siedem tysięcy"
If Arkusz33.Range("D8") = "68" Then Słownie = "sześćdziesiąt osiem tysięcy"
If Arkusz33.Range("D8") = "69" Then Słownie = "sześćdziesiąt dziewięć tysięcy"
If Arkusz33.Range("D8") = "70" Then Słownie = "siedemdziesiąt tysięcy"
If Arkusz33.Range("D8") = "71" Then Słownie = "siedemdziesiąt jeden tysięcy"
If Arkusz33.Range("D8") = "72" Then Słownie = "siedemdziesiąt dwa tysięce"
If Arkusz33.Range("D8") = "73" Then Słownie = "siedemdziesiąt trzy tysięce"
If Arkusz33.Range("D8") = "74" Then Słownie = "siedemdziesiąt cztery tysięce"
If Arkusz33.Range("D8") = "75" Then Słownie = "siedemdziesiąt pięć tysięcy"
If Arkusz33.Range("D8") = "76" Then Słownie = "siedemdziesiąt sześć tysięcy"
If Arkusz33.Range("D8") = "77" Then Słownie = "siedemdziesiąt siedem tysięcy"
If Arkusz33.Range("D8") = "78" Then Słownie = "siedemdziesiąt osiem tysięcy"
If Arkusz33.Range("D8") = "79" Then Słownie = "siedemdziesiąt dziewięć tysięcy"
If Arkusz33.Range("D8") = "80" Then Słownie = "osiemdziesiąt tysięcy"
If Arkusz33.Range("D8") = "81" Then Słownie = "osiemdziesiąt jeden tysięcy"
If Arkusz33.Range("D8") = "82" Then Słownie = "osiemdziesiąt dwa tysięce"
If Arkusz33.Range("D8") = "83" Then Słownie = "osiemdziesiąt trzy tysięce"
If Arkusz33.Range("D8") = "84" Then Słownie = "osiemdziesiąt cztery tysięce"
If Arkusz33.Range("D8") = "85" Then Słownie = "osiemdziesiąt pięć tysięcy"
If Arkusz33.Range("D8") = "86" Then Słownie = "osiemdziesiąt sześć tysięcy"
If Arkusz33.Range("D8") = "87" Then Słownie = "osiemdziesiąt siedem tysięcy"
If Arkusz33.Range("D8") = "88" Then Słownie = "osiemdziesiąt osiem tysięcy"
If Arkusz33.Range("D8") = "89" Then Słownie = "osiemdziesiąt dziewięć tysięcy"
If Arkusz33.Range("D8") = "90" Then Słownie = "dziewięćdziesiąt tysięcy"
If Arkusz33.Range("D8") = "91" Then Słownie = "dziewięćdziesiąt jeden tysięcy"
If Arkusz33.Range("D8") = "92" Then Słownie = "dziewięćdziesiąt dwa tysięce"
If Arkusz33.Range("D8") = "93" Then Słownie = "dziewięćdziesiąt trzy tysięce"
If Arkusz33.Range("D8") = "94" Then Słownie = "dziewięćdziesiąt cztery tysięce"
If Arkusz33.Range("D8") = "95" Then Słownie = "dziewięćdziesiąt pięć tysięcy"
If Arkusz33.Range("D8") = "96" Then Słownie = "dziewięćdziesiąt sześć tysięcy"
If Arkusz33.Range("D8") = "97" Then Słownie = "dziewięćdziesiąt siedem tysięcy"
If Arkusz33.Range("D8") = "98" Then Słownie = "dziewięćdziesiąt osiem tysięcy"
If Arkusz33.Range("D8") = "99" Then Słownie = "dziewięćdziesiąt dziewięć tysięcy"

'Setki
If Arkusz33.Range("E9") = "0" Then Słownie = Słownie & " "
If Arkusz33.Range("E9") = "1" Then Słownie = Słownie & " sto"
If Arkusz33.Range("E9") = "2" Then Słownie = Słownie & " dwieście"
If Arkusz33.Range("E9") = "3" Then Słownie = Słownie & " trzysta"
If Arkusz33.Range("E9") = "4" Then Słownie = Słownie & " czterysta"
If Arkusz33.Range("E9") = "5" Then Słownie = Słownie & " pięćset"
If Arkusz33.Range("E9") = "6" Then Słownie = Słownie & " sześćset"
If Arkusz33.Range("E9") = "7" Then Słownie = Słownie & " siedemset"
If Arkusz33.Range("E9") = "8" Then Słownie = Słownie & " osiemset"
If Arkusz33.Range("E9") = "9" Then Słownie = Słownie & " dziewięćset"

'Dziesiątki
If Arkusz33.Range("F10") = "0" Then Słownie = Słownie & " "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "0" Then Słownie = Słownie & " dziesięć złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "1" Then Słownie = Słownie & " jedenaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "2" Then Słownie = Słownie & " dwanaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "3" Then Słownie = Słownie & " trzynaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "4" Then Słownie = Słownie & " czternaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "5" Then Słownie = Słownie & " piętnaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "6" Then Słownie = Słownie & " szesnaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "7" Then Słownie = Słownie & " siedemnaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "8" Then Słownie = Słownie & " osiemnaście złotych "
If Arkusz33.Range("F10") = "1" And Arkusz33.Range("G11") = "9" Then Słownie = Słownie & " dziewiętnaście złotych "
If Arkusz33.Range("F10") = "2" Then Słownie = Słownie & " dwadzieścia"
If Arkusz33.Range("F10") = "3" Then Słownie = Słownie & " trzydzieści"
If Arkusz33.Range("F10") = "4" Then Słownie = Słownie & " czterdzieści"
If Arkusz33.Range("F10") = "5" Then Słownie = Słownie & " pięćdziesiąt"
If Arkusz33.Range("F10") = "6" Then Słownie = Słownie & " sześćdziesiąt"
If Arkusz33.Range("F10") = "7" Then Słownie = Słownie & " siedemdziesiąt"
If Arkusz33.Range("F10") = "8" Then Słownie = Słownie & " osiemdziesiąt"
If Arkusz33.Range("F10") = "9" Then Słownie = Słownie & " dziewięćdziesiąt"

'Jedności
If Arkusz33.Range("G11") = "0" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " złotych "
If Arkusz33.Range("G11") = "1" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " jeden złotych "
If Arkusz33.Range("G11") = "2" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " dwa złote "
If Arkusz33.Range("G11") = "3" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " trzy złote "
If Arkusz33.Range("G11") = "4" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " cztery złote "
If Arkusz33.Range("G11") = "5" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " pięć złotych "
If Arkusz33.Range("G11") = "6" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " sześć złotych "
If Arkusz33.Range("G11") = "7" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " siedem złotych "
If Arkusz33.Range("G11") = "8" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " osiem złotych "
If Arkusz33.Range("G11") = "9" And Arkusz33.Range("F10") <> "1" Then Słownie = Słownie & " dziewięć złotych "

'Warunek gdy zero złotych lub jeden złoty
If Arkusz33.Range("C5") < 1 And Arkusz33.Range("C5") > 0 Then Słownie = "zero złotych "
If Arkusz33.Range("C5") >= 1 And Arkusz33.Range("C5") < 2 Then Słownie = "jeden złoty "

'Grosze
If Arkusz33.Range("H13") = "" Then Słownie = Słownie & "0/100 groszy"
If Arkusz33.Range("H13") <> "" Then Słownie = Słownie & CCur(Format(Arkusz33.Range("H13"), "0.#0")) & "/100 groszy"

Słownie = "(Słownie:" & Słownie & ")"

Arkusz33.Range("C20").Value = Słownie

End Sub

Jeśli jesteś zainteresowany tematem uzyskania kwot słownie w excelu i makrem do ściągnięcia to oprócz tematów w tym dziale polecam artykuł z działu ZAGADNIENIA VBA - Kwota słownie w Excelu.

UWAGA:
Przygotowałem samodzielną wtyczkę rozszerzającą możliwości Excela między innymi o zapis słowny kwoty - opis na stronie Dodatek_GK. Zastosowanie wtyczki nie wymaga umiejętności programowania.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Kwota słownie w Excelu - algorytm krótki, ale trudniejszy

Przykład jak powinna wyglądać funkcja przedstawiająca zapis słowny zapisanej kwoty zaczerpnięty jest z ksiażki pana Sergiusza Flanczewskiego 'Excel z elementami VBA w firmie'.
Przykład tam zawarty zapisany jest jako funkcja Excel-a.
UWAGA - Polecam również temat na blogu p. Marcina Egerta - tutaj.

KWOTA SŁOWNIE w EXCELU - krótki Kliknij, żeby (roz)winąć listing

Function SLOX(parametr)

jedno = Array("", "jeden", "dwa", "trzy", "cztery", "pięć", "sześć", "siedem", "osiem", "dziewięć")
dzies = Array("", "dziesięć", "dzieścia", "dzieści", "dzieści", "dziesiąt", "dziesiąt", "dziesiąt", "dziesiąt", "dziesiąt",)
setki = Array("", "sto", "dwieście", "sta", "sta", "set", "set", "set", "set", "set")
od10_do19 = Array("dziesięć", "jedenaście", "dwanaście", "trzynaście", "czternaście", "piętnaście", "szesnaście", "siedemnaście", "osiemnaście", "dziewiętnaście")

slowo = ""
ciag = Fix(parametr)
wzorzec = Format(ciag, String(12, "0"))
czy_zero = parametr - Fix(parametr)
wzorzec2 = "0" + Mid(Format(czy_zero, "0.00"),3,2)
If czy_zero = 0 Then wzorzec2 = "000"
For k = 1 To 15 Step 3
ciag_3 = Mid(wzorzec, k, 3)
If k =13 Then ciag_3 = wzorzec2
s1 = ""
s2 = ""
s3 = ""
n1 = Val(Mid(ciag_3, 1, 1))
n2 = Val(Mid(ciag_3, 2, 1))
n3 = Val(Mid(ciag_3, 3, 1))
pomoc = Val(Mid(ciag_3, 2, 2))
s1 = jedno(n1)+setki(n1)
If n1 < 3 Then s1 = setki(n1)
s2 = jedno(n2)+dzies(n2)
s3 = jedno(n3)
If pomoc > 9 And pomoc < 20 Then
s2 = ""
s3 = od10_do19(n3)
End If
m = ""
If k = 1 And Val(ciag_3)> 0 Then m = "mld*"
If k = 4 And Val(ciag_3)> 0 Then m = "mln*"
If k = 7 And Val(ciag_3)> 0 Then m = "tys*"
If k = 10 And Val(ciag_3)> 0 Then m = "mld*"
If k = 13 And Val(ciag_3)> 0 Then m = "zł*"
If k = 13 Then m = "gr*"
If k = 13 And czy_zero = 0 Then m = "zerogr*"
slowo = slowo + s1 + s2 + s3 + m
Next k

If Val(ciag) = 0 Then slowo = "zerozł*" & slowo
If Val(ciag) = 0 And czy_zero = 0 Then slowo = "zerozł*zerogr*"
slox = slowo
End Function

Jeśli jesteś zainteresowany tematem uzyskania kwot słownie w excelu i makrem do ściągnięcia to oprócz tematów w tym dziale polecam artykuł z działu ZAGADNIENIA VBA - Kwota słownie w Excelu.

UWAGA:
Przygotowałem samodzielną wtyczkę rozszerzającą możliwości Excela między innymi o zapis słowny kwoty - opis na stronie Dodatek_GK. Zastosowanie wtyczki nie wymaga umiejętności programowania.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Lista rozwijana ComboBox - dostosowywanie zawartości KLIKNIJ by (roz)winąć

Lista rozwijana jest jednym z najczęściej stosowanych formantów. Najczęściej wartości, które może przyjmować dane są zakresem zamkniętym. Problem pojawia się, gdy nie wiemy gdzie lista rozwijana się skończy lub gdy w jednej liście rozwijanej w zależności od zaznaczonej opcji chcemy umieścić kilka różnych zakresów wartości. Formularz lokalu użytkowego Jako przykład takiej sytuacji przyjąłem formularz zastosowany w programie BAZA TELEADRESOWA. Jest to formularz wywoływany kliknięciem LPM na przycisku 'Raport lokalu użytkowego'. W zależności od zaznaczonej w ramce opcji (zakresu budynków) zmienia się zakres listy rozwijanej. Później wybranie jej wartości i użycie przycisku 'Szukaj lokalu' jest starterem, który wprowadza wyszukane dane adresowe najemcy lokalu użytkowego do etykiet formularza. Ważne jest by zwrócić uwagę na inne możliwe rozwiązanie zdarzenia ComboBox_Change. Po wybraniu z przycisku opcji zakresu budynków jako wartość ComboBox.Value wprowadzany jest napis 'Wybierz zakres budynków, a następnie lokal użytkowy'. Za chwilę po rozwinięciu listy wybranie adresu lokalu (czyli zdarzenie ComboBox_Change) może uruchomić procedurę wyszukania danych i wprowadzenia ich do etykiet formularza. Warto zauważyć, że VBA nie rozróżnia czy zmiana wartości ComboBox nastąpiła ręcznie czy w wyniku wykonania procedury. Aby obydwa te zdarzenia nie wywoływały szukania danych (takie zachowanie jest oczekiwane tylko w wyniku ręcznego wybrania) użycie przycisku opcji można 'ubrać' w ramy zmiany wartości komórki znacznikowej. W ustalonej komórce arkusza pomocniczego wprowadzić wartość np. 'N', a zdarzenie ComboBox_Change dla którego następuje wyszukanie danych warunkować wystąpieniem tej wartości w komórce. W przypadku zastania tam innej wartości zdarzenie nie uruchamia szukania. Dla przycisku opcji wstawić na początek zmianę wartości tej komórki znacznikowej - w ten sposób w chwili, gdy jako ComboBox.Value wprowadzana jest nowa wartość (napis) szukanie nie nastąpi ponieważ wartość komórki znacznikowej nie odpowiada wartości zdefiniowanej. Przypomina to wydruk z arkusza, który jest niewidoczny - całą procedurę zamykam w ramy poleceń: na początku ujawnij arkusz na końcu schowaj arkusz - jak w przykładzie Wydruk z poziomu Visual Basica tabeli o zmiennym zakresie wierszy/kolumn, opisanym powyżej.
To co jest ważne w tym przykładzie to rozwiązanie szukania końca listy ComboBox gdy znamy jej początek - zastosowano pętlę 'Do Loop Until' i podstawienie zmiennej Zakres_1 typu String. Nieco to przypomina wydruk zmiennego zakresu tabeli podany w uwadze wyżej.

PRZYCISK OPCJI Kliknij, żeby (roz)winąć listing

Private Sub OptionButton1_Click()

'ZAZNACZANIE ZAKRESU COMBOBOX1 - OR 11,12,13
Dim Pierwsza_1 As Range
Dim ss_1 As String
Dim s_1 As Single
Dim Zakres_1 As String

ComboBox1.Value = "Wybierz zakres budynków, następnie lokal użytkowy"
Set Pierwsza_1 = Arkusz15.Range("F57")
ss_1 = 0
s_1 = 55

Do
ss_1 = Pierwsza_1.Value
If ss_1 <> "" Then Set Pierwsza_1 = Pierwsza_1.Offset(1, 0)
If ss_1 <> "" Then s_1 = s_1 + 1
Loop Until ss_1 = "xxx"

Zakres_1 = "Lok_uzytk!F57:F"& s_1
ComboBox1.RowSource = Zakres_1
ComboBox1.Enabled = True
CommandButton1.Enabled = True
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
End Sub

PRZYCISK 'Szukaj lokalu' Kliknij, żeby (roz)winąć listing

Private Sub CommandButton1_Click()

'DEFINICJA ZMIENNYCH
Dim i As Integer
Dim adres As String, adres_OR As String, Data_lok_calosc As String, Dzialalnosc As String, Data_dzial As String, Dane_najemcy As String, Data_d_naj As String, Adres_najemcy_k As String, Miejsc_najemcy As String, Data_adres As String, Telefon_1 As String, Data_1 As String, Telefon_2 As String, Data_2 As String, Telefon_3 As String, Data_3 As String, Telefon_4 As String, Data_4 As String, Adres_mail As String, Data_mail As String, Notatka As String, s As String
Dim jest As Range

Label22.Visible = False

'POCZĄTEK POSZUKIWAŃ
s = ComboBox1.Value
If s = "Wybierz zakres budynków, następnie lokal użytkowy" Then
CommandButton3.Enabled = False
s = "KOMUNIKAT"
End If

Set jest = Arkusz15.Range("G3").CurrentRegion.Find(s, LookAt:=xlWhole)

If Not jest Is Nothing Then
With jest
adres = jest.Value
adres_OR = jest.Offset(0, -2).Value
Data_lok_calosc = jest.Offset(0, 1).Value
Dzialalnosc = jest.Offset(0, 2).Value
Data_dzial = jest.Offset(0, 3).Value
Dane_najemcy = jest.Offset(0, 4).Value
Data_d_naj = jest.Offset(0, 5).Value
Adres_najemcy_k = jest.Offset(0, 6).Value
Miejsc_najemcy = jest.Offset(0, 7).Value
Data_adres = jest.Offset(0, 8).Value
Telefon_1 = jest.Offset(0, 9).Value
Data_1 = jest.Offset(0, 10).Value
Telefon_2 = jest.Offset(0, 11).Value
Data_2 = jest.Offset(0, 12).Value
Telefon_3 = jest.Offset(0, 13).Value
Data_3 = jest.Offset(0, 14).Value
Telefon_4 = jest.Offset(0, 15).Value
Data_4 = jest.Offset(0, 16).Value
Adres_mail = jest.Offset(0, 17).Value
Data_mail = jest.Offset(0, 18).Value
Notatka = jest.Offset(0, 19).Value
Data_calosc = Arkusz2.Range("C13")

End With

If Telefon_1 = "" Then
Telefon_1 = "Brak telefonu"
End If
If Telefon_2 = "" Then
Telefon_2 = "Brak telefonu"
End If
If Telefon_3 = "" Then
Telefon_3 = "Brak telefonu"
End If
If Telefon_4 = "" Then
Telefon_4 = "Brak telefonu"
End If
If Adres_mail = "" Then
Adres_mail = "Brak adresu"
End If
If Adres_najemcy_k = "" Then
Adres_najemcy_k = "***************************************"
End If
If Miejsc_najemcy = "" Then
Miejsc_najemcy = "***************************************"
End If
If Adres_najemcy_k = "***************************************" And Miejsc_najemcy = "***************************************" Then
Label22.Visible = True
Label22.Enabled = True
End If

CommandButton1.Enabled = True
CommandButton2.Enabled = True
CommandButton3.Enabled = False
CommandButton4.Enabled = False
CommandButton5.Enabled = False
CommandButton6.Enabled = False

'WSTAWIENIE ZNALEZIONYCH ZMIENNYCH DO ODPOWIEDNICH ETYKIET
Label4.Enabled = True
Label5.Enabled = True
Label6.Enabled = True
Label27.Enabled = True
Label8.Enabled = True
Label9.Enabled = True
Label13.Enabled = True
Label14.Enabled = True
Label16.Enabled = True
Label29.Enabled = True

Label3.Visible = False
Label17.Visible = True
Label17.Enabled = True
Label18.Visible = True
Label18.Enabled = True
Label19.Visible = True
Label19.Enabled = True
Label26.Visible = True
Label26.Enabled = True
Label21.Visible = True
Label21.Enabled = True
Label24.Visible = True
Label24.Enabled = True
Label25.Visible = True
Label25.Enabled = True
Label28.Visible = True
Label28.Enabled = True

Label28.Caption = Dane_najemcy
TextBox2.Value = adres
Label3.Caption = adres
TextBox3.Value = Telefon_1
Label17.Caption = Telefon_1
TextBox4.Value = Telefon_2
Label18.Caption = Telefon_2
TextBox5.Value = Telefon_3
Label19.Caption = Telefon_3
TextBox11.Value = Telefon_4
Label26.Caption = Telefon_4
TextBox7.Value = Adres_mail
Label21.Caption = Adres_mail
TextBox9.Value = Adres_najemcy_k
Label24.Caption = Adres_najemcy_k
TextBox10.Value = Miejsc_najemcy
Label25.Caption = Miejsc_najemcy
TextBox1.Value = Notatka

TextBox2.Visible = False
TextBox3.Visible = False
TextBox4.Visible = False
TextBox5.Visible = False
TextBox11.Visible = False
TextBox7.Visible = False
TextBox9.Visible = False
TextBox10.Visible = False
TextBox1.Visible = True

TextBox1.Enabled = False

Frame1.Enabled = False
ComboBox1.Enabled = False
CommandButton1.Enabled = False
CommandButton3.Enabled = True
If s = "KOMUNIKAT" Then CommandButton3.Enabled = False
CommandButton5.Enabled = True
CommandButton6.Enabled = True

Else
i = MsgBox("Proszę wybrać lokal do wyszukania", vbOKOnly + vbCritical, "Uważaj!")

End If

End Sub

W punkcie przedstawiono sposób uzyskania zmiennej zawartości bardzo popularnej kontrolki ComboBox. Przykład pochodzi z jednego z moich programów, w którym na podstawie zaznaczonej opcji zmieniała się zawartość listy rozwijanej. Przedstawione rozwiązanie działa, ale nie jest jedynym - inne artykuły dotyczące ComboBox przedstawiam na podstronie 'Zagadnienia VBA':
- Formant ComboBox Excela
- Formanty ListBox i ComboBox w Excelu sposób wypełniania listy

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Sortowanie z poziomu Visual Basica

Przykład zaczerpnięty jest z pliku REJESTR ZAKUPÓW. W pliku tym istnieje możliwość wyboru kontrahenta z listy rozwijanej ComboBox. Formularz nowego kontrahenta Zakres tej listy tzn. zbiór kontrahentów jest definiowalny tzn. można zmienić dane kontrahenta lub wprowadzić nowego na listę. Obydwu tych operacji dokonuje się z menu rozwijanego 'Kontrahenci' zastępującego menu MS Excel. Aby na liście rozwijanej nie było chaosu, należy po wprowadzeniu nowego kontrahenta nie tylko rozszerzyć jej zakres, ale także przesortować nową, dłuższą listę tak by cała jej zawartość ułożona była alfabetycznie. Na ilustracji pokazany jest omawiany formularz w chwili uruchomienia. Po wypełnieniu pól tekstowych odblokowany zostanie przycisk CommandButton 'Wstaw dane'. Pod zdarzeniem dla tego przycisku tj. CommandButton_Click załączona jest procedura ustalenia zakresu nowej listy i sortowania.

SORTOWANIE - PRZYKŁAD Kliknij, żeby (roz)winąć listing

Private Sub CommandButton1_Click()

Dim i As Integer
Dim Pierwszy_wolny As Range
Dim ss_1 As String
Dim Pierwsza_1 As Range
Dim dd_1 As String
Dim d_1 As Single
Dim Zakres_1 As String
Dim Zakres_sortu As Range
Dim Tekst As String
Dim Składnik_1 As Range, Składnik_2 As Range, Składnik_3 As Range
Dim Nazwa As Range, NIP As Range

On Error GoTo Et:

Arkusz18.Range("A1").Value = "Zmiana"

'Ustalenie ostatniego wiersza
Set Pierwszy_wolny = Arkusz18.Range("C5")
ss_1 = 0

Do
ss_1 = Pierwszy_wolny.Value
If ss_1 <> "" And ss_1 <> " " Then Set Pierwszy_wolny = Pierwszy_wolny.Offset(1, 0)
Loop Until ss_1 = " "

'Wstawianie wartości
Pierwszy_wolny.Offset(0, 1).Value = TextBox4.Value
Pierwszy_wolny.Offset(0, 4).Value = TextBox1.Value
Pierwszy_wolny.Offset(0, 5).Value = TextBox2.Value
Pierwszy_wolny.Offset(0, 6).Value = TextBox3.Value

'Początek sortowania
Set Pierwsza_1 = Arkusz18.Range("C5")
dd_1 = 0
d_1 = 3

Do
dd_1 = Pierwsza_1.Value
If dd_1 <> "" Then Set Pierwsza_1 = Pierwsza_1.Offset(1, 0)
If dd_1 <> "" Then d_1 = d_1 + 1
Loop Until dd_1 = " "

Zakres_1 = "Firmy!C5:J" & d_1
Set Zakres_sortu = Arkusz18.Range(Zakres_1)

Zakres_sortu.Sort Key1:=Arkusz18.Range("C5"), Order1:=xlAscending, Header:=xlYes, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

'Składanie tekstów dla całości

Set Składnik_1 = Arkusz18.Range("G5")
Set Składnik_2 = Arkusz18.Range("H5")
Set Składnik_3 = Arkusz18.Range("I5")
Set Nazwa = Arkusz18.Range("J5")
Set NIP = Arkusz18.Range("D5")

Do
Tekst = Składnik_1.Value & Chr(10) & Składnik_2.Value & Chr(10) & Składnik_3.Value
Nazwa.Value = Tekst
Set Składnik_1 = Składnik_1.Offset(1, 0)
Set Składnik_2 = Składnik_2.Offset(1, 0)
Set Składnik_3 = Składnik_3.Offset(1, 0)
Set Nazwa = Nazwa.Offset(1, 0)
Loop Until Składnik_1.Value = ""
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

'ZAZNACZANIE ZAKRESU COMBOBOX1
Dim s_1 As Single

Set Pierwsza_1 = Arkusz18.Range("C5")
ss_1 = 0
s_1 = 3

Do
ss_1 = Pierwsza_1.Value
If ss_1 <> "" Then Set Pierwsza_1 = Pierwsza_1.Offset(1, 0)
If ss_1 <> "" Then s_1 = s_1 + 1
Loop Until ss_1 = " "

Zakres_1 = "Firmy!C5:C" & s_1
Arkusz1.ComboBox1.ListFillRange = Zakres_1
Arkusz2.ComboBox1.ListFillRange = Zakres_1
Arkusz3.ComboBox1.ListFillRange = Zakres_1
Arkusz4.ComboBox1.ListFillRange = Zakres_1
Arkusz5.ComboBox1.ListFillRange = Zakres_1
Arkusz6.ComboBox1.ListFillRange = Zakres_1
Arkusz7.ComboBox1.ListFillRange = Zakres_1
Arkusz8.ComboBox1.ListFillRange = Zakres_1
Arkusz9.ComboBox1.ListFillRange = Zakres_1
Arkusz10.ComboBox1.ListFillRange = Zakres_1
Arkusz11.ComboBox1.ListFillRange = Zakres_1
Arkusz12.ComboBox1.ListFillRange = Zakres_1
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

'Odblokowanie
Arkusz18.Range("A1").Value = "Praca"

CommandButton1.Enabled = False

Exit Sub
Et: Komunikat = MsgBox("Zamknij formularz i otwórz ponownie", vbOKOnly + vbCritical, "Dane nowego kontrahenta")

End Sub

Materiał dotyczący najczęściej stosowanych algorytmów sortowania tj. sortowanie bąbelkowe i arkuszowe, razem z pomiarem czasu wykonania operacji podaję w dziale Zagadnienia VBA 'Makro do sortowania tablic z poziomu VBA'.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wydruk tabel Excela z formułami i sumowaniem/kopiowaniem i usuwaniem wierszy

Zagadnienie to w praktyce wystąpiło przy okazji wykonywania pliku REJESTR ZAKUPÓW. Ze względu na wymagania odbiorcy pliku, tabela robocza miała być przez cały czas pracy widoczna. Przygotowałem ją, wprowadzając odpowiednie formuły, na ilość wierszy, która nigdy nie zostanie przekroczona. Na dole znajduje się wiersz podsumowania. Wydruk powinien wyglądać w ten sposób, że wszystkie niewykorzystane wiersze są usuwane. Wykonanie programowe bez problemów, jednak kłopot byłby gdyby po wykonaniu wydruku użytkownik chciał uzupełnić zestawienie (musiałby wstawić wiersze i uporządkować formuły - czego nie lubi) oraz w sytuacji, gdy ten plik byłby przygotowywany na następny rok (czyszczone wszystkie pola zestawień itp.). Znacznie bardziej eleganckie i niewidoczne dla użytkownika jest, obecnie przyjęte, rozwiązanie tj. drukowany arkusz najpierw jest programowo kopiowany do wcześniej ustalonego, niewidocznego podczas normalnej pracy arkusza roboczego, a później wszystkie operacje przebiegają już tylko w tym arkuszu roboczym. W ten sposób jeżeli użytkownik chce jakiś wiersz dopisać nie ma problemu - po prostu dopisze i użyje przycisku 'Drukuj' jeszcze raz. Z przygotowaniem pliku następnego roku też nie ma problemu - ponieważ układ tabel miesięcznych pozostał bez zmian wyczyszczenie ich jest działaniem powtarzalnym.
Poniżej listing procedury pod przyciskiem 'Drukuj'

WYDRUK TABEL - styczeń Kliknij, żeby (roz)winąć listing

Sub Wydruk_styczeń_neu()
'
Dim Kontrola_zgodności As Range
Dim Tekst_1 As String, Tekst_2 As String
Dim i As Integer, Komunikat As Integer

Set Kontrola_zgodności = Arkusz1.Range("BA6")
Tekst_1 = "Wprowadzone wartości faktur zgodne z sumą kwot rozbicia" & Chr(10) & "Po zamknięciu okna nastąpi drukowanie"
Tekst_2 = "Niezgodności kwot faktur w następujących pozycjach:"

For i = 6 To 103
If Kontrola_zgodności.Value = 0 Then
Tekst_2 = Tekst_2 & Chr(10) & Kontrola_zgodności.Offset(0, -51).Value
End If
Set Kontrola_zgodności = Kontrola_zgodności.Offset(1, 0)
Next

If Tekst_2 = "Niezgodności kwot faktur w następujących pozycjach:" Then
Komunikat = MsgBox(Tekst_1, vbOKOnly + vbInformation, "Sprawdzenie zgodności kwot")
Call Druk_szablon_styczeń
End If

If Tekst_2 <> "Niezgodności kwot faktur w następujących pozycjach:" Then
Tekst_2 = Tekst_2 & Chr(10) & "Druk zablokowany"
Komunikat = MsgBox(Tekst_2, vbOKOnly + vbCritical, "Sprawdzenie zgodności kwot")
End If

End Sub

PODPROCEDURA 'Call Druk_szablon_styczeń' Kliknij, żeby (roz)winąć listing

Sub Druk_szablon_styczeń()
'
Dim licznik As Integer, i As Integer, a As Integer
Dim Źródło As Range, Docelowa As Range
Dim Zakres_druku As Areas
Dim Obszar_druku As String

'Kopiowanie szablonu tabeli
Arkusz20.Cells.Copy
'Sheets("Wydruk").Select
Worksheets("Wydruk").Cells.PasteSpecial
'Arkusz19.Cells.PasteSpecial

'Kopiowanie elementów stałych tabeli
Arkusz19.Range("B1").Value = Arkusz1.Range("B1").Value
Arkusz19.Range("G104").Value = Arkusz1.Range("G104").Value
Arkusz19.Range("H104").Value = Arkusz1.Range("H104").Value
Arkusz19.Range("I104").Value = Arkusz1.Range("I104").Value
Arkusz19.Range("J104").Value = Arkusz1.Range("J104").Value
Arkusz19.Range("K104").Value = Arkusz1.Range("K104").Value
Arkusz19.Range("L104").Value = Arkusz1.Range("L104").Value
Arkusz19.Range("M104").Value = Arkusz1.Range("M104").Value
Arkusz19.Range("N104").Value = Arkusz1.Range("N104").Value
Arkusz19.Range("M105").Value = Arkusz1.Range("M105").Value
Arkusz19.Range("H106").Value = Arkusz1.Range("H106").Value
Arkusz19.Range("K106").Value = Arkusz1.Range("K106").Value

'Kopiowanie tekstu tabeli
Set Źródło = Arkusz1.Range("C6")
Set Docelowa = Arkusz19.Range("C6")
licznik = 6

For i = 6 To 103
If Źródło.Value <> "" Or Źródło.Offset(0, 1).Value <> "" Or _
Źródło.Offset(0, 2).Value <> "" Or Źródło.Offset(0, 3).Value <> "" Or _
Źródło.Offset(0, 4).Value <> "" Or Źródło.Offset(0, 5).Value <> "" Or _
Źródło.Offset(0, 6).Value <> "" Or Źródło.Offset(0, 7).Value <> "" Or _
Źródło.Offset(0, 8).Value <> "" Or Źródło.Offset(0, 9).Value <> "" Or _
Źródło.Offset(0, 10).Value <> "" Or Źródło.Offset(0, 11).Value <> "" Or _
Źródło.Offset(0, 12).Value <> "" Or Źródło.Offset(0, 13).Value <> "" Then
Docelowa.Offset(0, -1).Value = Źródło.Offset(0, -1).Value
Docelowa.Value = Źródło.Value
Docelowa.Offset(0, 1).Value = Źródło.Offset(0, 1).Value
Docelowa.Offset(0, 2).Value = Źródło.Offset(0, 2).Value
Docelowa.Offset(0, 3).Value = Źródło.Offset(0, 3).Value
Docelowa.Offset(0, 4).Value = Źródło.Offset(0, 4).Value
Docelowa.Offset(0, 5).Value = Źródło.Offset(0, 5).Value
Docelowa.Offset(0, 6).Value = Źródło.Offset(0, 6).Value
Docelowa.Offset(0, 7).Value = Źródło.Offset(0, 7).Value
Docelowa.Offset(0, 8).Value = Źródło.Offset(0, 8).Value
Docelowa.Offset(0, 9).Value = Źródło.Offset(0, 9).Value
Docelowa.Offset(0, 10).Value = Źródło.Offset(0, 10).Value
Docelowa.Offset(0, 11).Value = Źródło.Offset(0, 11).Value
Docelowa.Offset(0, 12).Value = Źródło.Offset(0, 12).Value
Docelowa.Offset(0, 13).Value = Źródło.Offset(0, 13).Value
licznik = licznik + 1
End If

Set Źródło = Źródło.Offset(1, 0)
Set Docelowa = Docelowa.Offset(1, 0)
Next
'Usunięcie pustych wierszy
For a = licznik To 103
Arkusz19.Rows(licznik).EntireRow.Delete
Next

'Drukowanie
Arkusz19.Visible = xlSheetVisible

Obszar_druku = "='Wydruk'!R2C2:R" & licznik + 2 & "C16"
'
Worksheets("Wydruk").Names.Add Name:="Zakres_druku", RefersToR1C1:= _
Obszar_druku

Worksheets("Wydruk").PageSetup.PrintArea = "Zakres_druku"

Worksheets("Wydruk").PrintOut Copies:=1, Collate:=True

Arkusz19.Visible = xlSheetHidden

End Sub

Uwaga: przed dokonaniem wydruku program sprawdza zgodność kwot - jeżeli jest niezgodna odmawia wydruku, ale wskazuje pozycję lub pozycje, w których znalazł błędy. Jeszcze jedna sprawa: w VBA znak podkreślenia stosuje się przy przenoszeniu polecenia do następnej linii - jeżeli polecenia piszemy w jednej - znaku tego się nie stosuje. Przy czym np. dla rozbudowanych warunków logicznych - w przykładzie dla pętli 'If Then' - pisanie w jednej linii jest niepraktyczne.
Dla osób zainteresowanych tematem wydruków VBA polecam artykuł z działu 'Zagadnienia VBA':
Wydruk zakresu arkusza z poziomu VBA ,
Sterowanie Wordem z poziomu Excela.
Na w/w podstronie zawarty jest też artykuł Wywoływanie procedur z innych procedur - zastosowanie instrukcji Call.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Wersje MS Excel i różnice w edytorach VBA

Ciężki temat: przy programach można się napracować i później z drżeniem wyczekiwać czy na wszystkich komputerach zadziałają. Swoje prace wykonywałem na MS Excel 2003 (czasem 2000) oraz MS Excel 2007 całość pod systemem Windows XP (po Viście mam do dziś czkawkę). Trzy moje programy w excelu pracują w sieci w trybie pliku udostępnionego, a korzystają z nich użytkownicy z Excelem 2003 i 2007 (edycja poniżej profesjonalnej). W tym układzie z dwoma plikamu nie ma większych zgrzytów i problemów, ale dla trzeciego pliku tj. BAZY TELEADRESOWEJ pojawiają się dość poważne kłopoty. Excel 2007 dość irytująco ucina napisy na przyciskach menu głównego (wstawiane w polu tekstowym) - specjalnie dla niego zmniejszałem czcionkę lub zwiększałem nieco pole tekstowe. Zapisane pliki otwierane są w obydwie strony tj. zapisane w 2003 otwierane pod 2007 i zapisane w 2007 otwierane pod 2003, przy czym Excel 2007 w opcjach ustawiony jest 'działaj w trybie zgodności z Excelem 97-2003'. Prawdziwy problem jest z Excelem 2007 edycja profesjonalna: odczyta wszystkie pliki, ale cokolwiek zapisze jest nie do ruszenia na żadnym innym komputerze (nie tylko z Officem 2003, ale i 2007 Small). Komunikat przy próbie wejścia do pierwszego przycisku mówi o błędzie kodowania w ukrytym module itp. Przy próbie dojścia o co chodzi Office jako źródło swoich problemów podał znak Chr(10) tj. przejścia do następnej linii, który obydwa Officy respektują - czyli kolejny bezsensowny komunikat. Excel 2007 profesjonalny działa w trybie zgodności, ale to nie pomaga - nie pomaga nawet gdy plik tylko otworzę nawet go nie zapisując - skażenie czystości już się dokonało :( Dla programu BAZA TELEADRESOWA zdarza się, że plik zapisany pod 2007 Small, można użytkować pod 2003 Small Business, ale pod 2003 edycja Basic już nie. Co ciekawe, zapisanie pliku pod 2003 Small Business nie poprawia sytuacja - nadal 2003 edycja Basic jest poszkodowana. Niezrozumiałe dla mnie jest, dlaczego program WYKUP GRUNTÓW oraz pozostałe formularze BAZY TELEADRESOWEJ nie zepsuły się jeszcze nigdy. Ponieważ program BAZA TELEADRESOWA był wielokrotnie modyfikowany, a nieużywane algorytmy i formularze w nim pozostały, planuję napisać go na nowo, optymalizując kod (skrócenie długości makr, wyrzucenie starych formularzy, stosowanie krótkich procedur zewnętrznych wywoływanych przez Call itp.). Mam nadzieję, że to pomoże.
Podsumowując - należy uważać na Excel 2007 edycja profesjonalna oraz na długie skomplikowane algorytmy (BAZA TELEADRESOWA to około sześćset stron kodu).

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Jak dostosować plik Excela do pracy w sieci?

Kolejny etap rozwoju programu pod Excelem to próba jego usieciowienia. Najprościej to wykonać wprowadzając opcję pracy wielu użytkowników - dla Excela 2003 wygląda to tak:
Menu górne Narzędzia / Udostępnij skoroszyt ... / opcja 'Pozwalaj na zmiany wprowadzane jednocześnie przez wielu użytkowników' i w zasadzie koniec.
Tak przygotowany plik wrzucam do swojego folderu udostępnionego, a wszystkim użytkownikom robię skrót na pulpit. Dla Excela 2007 działa to tak samo, tyle że dla mnie szukanie czegoś na 'wstążce szybkiego dostępu' to dość frustrujące zajęcie. Ustawienie pliku udostępnionego wygląda natomiast tak samo.
Plik udostępniony narzuca jednak pewne ograniczenia przy pracy makropoleceń:
- nie można edytować pliku pod VBA (aby nanosić jakiekolwiek poprawki trzeba wyłączyć funkcję 'udostępnij skoroszyt'),
- w pliku udostępnionym nie da się zablokować i odblokować arkusza na hasło, nawet po podaniu poprawnego słowa (polecenie Arkusz1.Protect ([Haslo]) i oczywiście Arkusz1.Unprotect). Z tego względu lepiej dokładnie rozważyć, czy wykonywany przez nas program będzie działał w sieci czy też nie. Zabezpieczenie na hasło można bez problemu wykonać ograniczając dostęp do formularzy, a nie do arkuszy (tak obecnie funkcję tę realizuję),
- należy pamiętać, że aktualizacja danych w pliku następuje z chwilą jego zapisu. Dla takiego programu jak WYKUP GRUNTÓW dla uniknięcia nieporozumień zdecydowałem się wprowadzić polecenie 'Save' do zdarzenia inicjalizacji formularza - wydłuża to działanie programu, ale czyni go bardziej bezpiecznym.
Artykuł zbliżony tematycznie do niniejszego punktu zawarty jest w dziale Porady Excel 'Udostępnianie skoroszytu, arkusza lub części komórek'.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

Ustawienia wstępne wydruku, a szybkość działania programu VBA

Temat dosyć może ogólny, ale w praktyce ważny. W zasadzie z każdego prezentowanego na tej stronie programu możemy uzyskać wydruki. Szablony wydruków są określone, sam proces drukowania polega na wypełnieniu wolnych miejsc np. wynikami szukania i puszczeniu całości na drukarkę. W VBA można programowo ustawić chyba wszystkie opcje związane z wydrukami: obszar drukowany, ustawienia strony, powiększenie, marginesy, stopki, nagłówki, numerację i podział stron, powtarzanie wierszy, kolumn itd. itp. Należy jednak pamiętać o tym, by jak najwięcej danych ustawić bezpośrednio w Excelu. Arkusz, z którego raz już drukowano zachowuje te ustawienia - są one zapisywane w pliku. Ustawienie programowe opcji wydruku jest możliwe, ale długotrwałe - aż trudno czasami uwierzyć, jak bardzo. Po kilku różnych próbach nauczyłem się, że jedyne co ustawiam programowo do wydruku to zakres. Całą resztę formatuję na poziomie arkusza roboczego. Jeżeli z jednego arkusza bedą drukowane dwie tabele z czego jedna ustawiona jest na 90% wielkości, a druga 85% wielkości to przeniosę jedną z tych tabel do innego arkusza - tylko ze względu na sprawność wydruku. Na pewno zależy to od ustawień sieciowych, drukarki (a raczej jej pamięci) itd., ale zdarzyło mi się już czekać prawie trzy minuty aż komputer wyśle z poziomu VBA tabelę do wydruku. Tak długi okres oczekiwania pogarszałby odbiór naszych programów, więc jeszcze raz - w makropoleceniach tylko zakres, reszta opcji wydruku w arkuszach roboczych!
Dla osób zainteresowanych tematem wydruków VBA polecam artykuł z działu Zagadnienia VBA:
Wydruk zakresu arkusza z poziomu VBA ,
Sterowanie Wordem z poziomu Excela.

Do uwag wstępnych Do góry podstrony Do zapytań Do strony głównej

© 2009-2010 G. Koralewski design by styleshout.