background image

 

Rozdział 12 

Raporty 

W rozdziale tym będzie kontynuowana faza konstrukcyjna projektu RENTMAN 
(praca nad systemem raportów). Zaprojektujemy trzy sprawozdania i połączymy je 
z aplikacją. Będą to konkretnie: 

„Raport w postaci formularzy do drukowania zleceń prac. 

„Raport kolumnowy, prezentujący listę nieruchomości z tabeli PROPERTY. 

„Raport kolumnowy, zawierający listy z zestawieniami zadań dla pracowników. 

Metody konstruowania raportów przy pomocy Delphi 

Delphi udostępnia trzy podstawowe metody budowy raportów: konstruowanie 
formularzy nadających się do drukowania, tworzenie sprawozdań przy pomocy 
komponentu 

QuickReport

 oraz pisanie oprogramowania w języku Object 

Pascal-a, którego efektem będzie powstanie odpowiedniego raportu. 
W omówionym przypadku wykorzystamy wszystkie trzy sposoby. 

UWAGA 

Na liście narzędzi do tworzenia raportów brak czwartej kategorii, a mianowicie 
komercyjnych kreatorów raportów typu Crystal Reports, R&R czy ReportSmith. 
Przyczyną jest ich nieobecność w pakiecie Delphi. Borland postanowił wyposażyć 
Delphi we własne narzędzia do tworzenia raportów. Uważa się,  że najnowszy 
QuickReports jest porównywalny z 

programem ReportSmith, jeśli chodzi 

o prostotę użycia, natomiast pod względem mocy produkowane raporty są szybsze 
i silniejsze. 

Rodzaje raportów 

Sprawozdania tworzone dla potrzeb różnych organizacji można podzielić na cztery 
typy podstawowe: formularze, etykiety, raporty kolumnowe i 

zestawienia 

krzyżowe.  

background image

386 

Część II 

Sprawozdania w postaci formularza drukują tylko jeden rekord na stronie (lub 
jeden rekord główny i korespondujące z nim rekordy szczegółowe). Przykładem 
tego typu jest faktura - na jednej stronie drukowany jest tylko jeden rachunek. 

Raport kolumnowy jest tradycyjnym sprawozdaniem. Zawiera listę danych w serii 
równoległych kolumn. Zazwyczaj na jednej stronie znajduje się wiele rekordów. 
Ten typ zestawień może grupować dane oraz sumować zawartości kolumn. 
Przykładem takiego raportu jest lista pracowników przedsiębiorstwa. 

Trzeci typ raportów to wydruki etykiet, które są przeznaczone do masowego 
adresowania lub podobnych celów. Użytkownik określa rodzaj etykiety i ich liczbę 
na stronie. Zazwyczaj etykiety są drukowane na specjalnym papierze, 
umożliwiającym  łatwe przylepianie. Dobrym przykładem są wydruki tabeli 
CUSTOMER dostarczające klientom rozliczenia w postaci wyciągów. Próbkę 
etykiet pocztowych, powstałych przy pomocy narzędzia QuickReports, można 
znaleźć w składnicy obiektów Delphi. Aby się do nich dostać, wystarczy wybrać 
opcję 

File\New\Forms

Zestawienia krzyżowe obrazują dane w sposób wykorzystywany w arkuszach 
kalkulacyjnych. Różnica między tym typem sprawozdania a 

raportem 

kolumnowym jest istotna. Przykładem może być zestawienie sprzedaży 
poszczególnych oddziałów przedsiębiorstwa w rozbiciu na kolejne miesiące. 
Rubryki pionowe przedstawiają sprzedaż wszystkich oddziałów, a 

wiersze 

dokumentują wyniki kolejnych miesięcy. Informacje o osiągnięciach konkretnego 
oddziału w określonym miesiącu znajdujemy na przecięciu odpowiednich linii 
pionowych i poziomych. 

Poniższe przykłady pozwolą zapoznać się z metodami tworzenia wszystkich 
rodzajów raportów. Zestawienie prac zaprojektujemy jako raport w postaci 
formularza (wydrukowanie blankietu fmRWORMED0). Nauczymy się drukować 
proste sprawozdania tego typu, jedynie przy użyciu formularzy Delphi 
i standardowych komponentów. 

Wykaz nieruchomości z tabeli PROPERTY sporządzimy przy pomocy raportu 
kolumnowego, używając do jego budowy narzędzia QuickReport. Z pewnością 
większość sprawozdań  będzie sporządzana przez użytkowników Delphi przy 
pomocy tego komponentu. 

W rozdziale 19 skonstruujemy zestawienie krzyżowe, stosując do jego budowy 
element Delphi o nazwie 

DecisionCube

Niniejszy rozdział zakończymy zbudowaniem listy zleceń, wykorzystując jedynie 
kod Object Pascala. Zaznajomimy się w ten sposób z tradycyjnym sposobem 
sporządzania raportów. Do formatowania i 

drukowania sprawozdania 

wykorzystamy proste konstrukcje pętli i instrukcje obsługujące wyjścia terminala. 

background image

 Raporty 

387

 

Raport Work Order 

Pracę z 

raportami rozpoczniemy od przygotowania do druku formularza 

kombinowanego 

fmRWORMDE0

Najpierw wczytujemy do projektanta formularzy główne okno dialogowe aplikacji 
RENTMAN-

fmRSYSMAN0

. Na formularzu umieszczamy komponent 

PrinterSetupDialog

 i nadajemy mu nazwę 

ps_RENTMAN

. W menu 

File

 

aplikacji RENTMAN klikamy podwójnie na opcji

 Print Setup

 i wpisujemy linię 

kodu: 

ps_RENTMAN.Execute;

 

Następnie powracamy do projektanta i łączymy klawisz szybkiego dostępu 

sbPrintSetup

 z metodą obsługującą zdarzenie 

OnClick

  właściwej pozycji 

menu (

Print setup

). Okno dialogowe 

Print setup

  będzie wyświetlane zarówno 

wskutek wciśnięcia przycisku, jak i dzięki wyborowi właściwej propozycji z menu. 

Okno dialogowe 

Print Setup

 pozwala między innymi na zmianę drukarki 

przeznaczonej do drukowania zlecenia. Wszystkie aplikacje dla Windows, które 
umożliwiają drukowanie, zawierają okno do konfigurowania drukarek. 

Konfiguracja opcji Work Order w menu Report  

Połączymy teraz opcję 

Work Order

 z menu raportów z odpowiednią pozycją menu 

Tables. Formularz kombinowany będzie miał własne narzędzia umożliwiające jego 
wydruk, więc wybór opcji 

Work Order

 z oferty 

Report

 ma spowodować jedynie 

wyświetlenie formularza wyboru- 

fmRWORGRD0

. Ponieważ wywołanie 

formularza umożliwia już właściwa pozycja menu 

Tables

, to wystarczy połączyć 

odpowiadające sobie opcje obu menu. 

Przycisk drukowania 

Otwieramy 

fmRWORDMD0

 w projektancie formularzy i bezpośrednio po lewej 

stronie przycisku 

OK

 umieszczamy komponent 

BitBn

 (na górnym panelu 

formularza). Zmieniamy nazwę klawisza na 

bbPrint

, nagłówek na 

&Print

 oraz 

dołączamy ikonkę Images\Buttons\print.bmp (katalog Images powinien znajdować 
się w głównej kartotece Delphi). Dwukrotnie klikamy na przycisku, aby wpisać 
kod obsługujący zdarzenie 

OnClick

. Przy pomocy edytora kodu należy przepisać 

zawartość listingu 12.1. 

Listing 12.1. Drukowanie formularza fmRWORDMD0. 

Tag := Longint(WindowState); 
  WindowState:=wsMaximized; 
  for I:=0 to ComponentCount-1 do 

background image

388 

Część II 

    If (Components[I] is TButton) or 
       (Components[I] is TDBNavigator) or 
       (Components[I] is TSpeedButton) then 
         With Components[I] as TControl do begin 
           Tag:=Longint(Visible); 
           Visible:=False; 
         end; 

Print; 

for I:=0 to ComponentCount-1 do 
    If (Components[I] is TButton) or 
       (Components[I] is TDBNavigator) or 
       (Components[I] is TSpeedButton) then 
         With Components[I] as TControl do 
           Visible:=Boolean(Tag); 
  WindowState:=TWindowState(Tag); 

Należy się upewnić,  że zmienna 

I

 została zadeklarowana w nagłówku procedury 

jako wartość typu Integer. Prawidłowa definicja może wyglądać następująco: 

procedure TfmRWORDMD0.bbPrintClick (Sender: Tobject);

 

var

 

 

I: Integer;

 

Procedura ma trzy podstawowe zadania. Pierwsze polega na ukryciu komponentów 
formularza, których nie chcemy drukować w 

raporcie (wydrukowanie 

w sprawozdaniu przycisków nie miałoby najmniejszego sensu). Drugi cel to 
wydrukowanie formularza przy pomocy metody 

Print

, która wysyła do kolejki 

drukarki kopię formularza wyświetlanego na ekranie. Ostatecznie ukryte 
komponenty należy przywrócić z powrotem do poprzedniej postaci. 

Zwróćmy uwagę na klika cech napisanego kodu. Zacznijmy od omówienia 
istotnego wykorzystania własności 

Tag

. Jest to zmienna typu Long Integer, 

należąca do wielu komponentów Delphi, której nie jest przypisane żadne zadanie. 
Możemy ją wykorzystać w dowolny sposób. Nasz podprogram używa zmiennej na 
dwóch różnych poziomach: do zapisania/odtworzenia stanu okna 
(

WindowState

) oraz do zapisywania/odtwarzania własności 

Visible

 każdego 

z komponentów.  Chociaż w 

wielu przypadkach można by się obejść bez 

zachowywania i 

odświeżania poszczególnych elementów, to w 

niektórych 

sytuacjach skończyłoby się to otrzymaniem nieoczekiwanej postaci formularza. 
Nie zawsze bowiem 

WindowState

 będzie ustawiona w czasie inicjalizacji jako 

wsNormal

, gdyż możemy uznać,  że lepiej jest powiększyć okno do pełnego 

ekranu. Nie zawsze również, po uruchomieniu formularza, wszystkie jego 
komponenty muszą być widoczne, gdyż z jakiegoś powodu możemy chcieć ukryć 
niektóre z nich. Jeśli przyjmiemy założenie,  że wszystkie komponenty są 

background image

 Raporty 

389

 

widoczne, to po wydrukowaniu formularza i odtworzeniu ukrytych elementów, 
zobaczymy na ekranie również te, których nie oczekujemy. Rozwiązanie przyjęte 
w listingu 12.1 jest zdecydowanie lepsze. 

Innym ważnym aspektem procedury jest wykorzystanie własności 

Components

Components

 jest indeksowaną  własnością sterowników, które mogą zawierać 

inne elementy, na przykład: 

TForm

. Napisany kod używa jej do przejrzenia 

wszystkich komponentów formularza, określenia tych, które powinny być 
wydrukowane i 

odpowiedniego ustawienia ich własności 

Visible

Components

 można wykorzystywać do szybkiego konfigurowania własności  

grupy sterowników, w czasie pracy programu. 

Trzecim istotnym elementem przedstawionego programu jest używanie informacji 
o typie danych w czasie działania programu (RTTI- Runtime Type Information), 
między innymi w konstrukcjach 

is

 oraz 

with ... as

. Mimo że kompilator 

Delphi wytwarza kod maszynowy procesora, to w czasie pracy programu mamy 
możliwość określania typów danych jego specyficznych klas. Nie jest to oczywiste 
w przypadku kompilatorów kodu procesora. Informacje RTTI były kiedyś 
wyłączną domeną interpreterów, takich jak Visual Basic. Programy generujące kod 
procesora nie dają zwykle możliwości sprawdzania typu danych poszczególnych 
elementów, ponieważ język maszynowy nie zawiera takich informacji. Zmienne są 
redukowane do adresów względnych (offsets) i podczas tłumaczenia zamieniane na 
kod wynikowy. Zdolność Delphi do wykorzystywania informacji o typie danych 
pozwala nam korzystać z możliwości obu systemów: dysponujemy jednocześnie 
skompilowanym kodem procesora oraz przydatnymi informacjami o typie danych 
w czasie wykonywania programu. Dzięki temu jesteśmy w stanie sprawdzać 
komponenty formularza i odpowiednio je modyfikować. 

Czwartym, godnym odnotowania aspektem napisanej procedury jest selekcja 
wykonywana przed ustawianiem własności 

Visible

 każdego elementu. Nie 

wszystkie komponenty mają  własność 

Visible

 (na przykład  żaden 

z komponentów niewidzialnych). Nie można zatem napisać po prostu: 

with Components[I] do begin 
 Tag:=Longint(Visible); 
 Visible:=False; 
end; 

Ponieważ podstawowa klasa komponentu nie ma własności 

Visible

, wcześniej 

napisany kod nie powoduje ustawienia własności 

Visible

 wszystkich 

elementów 

Components[I]

. Gdybyśmy tak postąpili, to zaśmiecilibyśmy 

następny poziom, który jest klasą formularza, gdyż działalibyśmy wewnątrz 
metody formularza. Rezultatem byłoby ustawienie własności 

Visible

 

formularza, zupełnie przez nas niepożądane. 

background image

390 

Część II 

Przeglądarka obiektów 

Aby uniknąć  błędów, powinniśmy znaleźć klasę rodzicielską, która definiuje 
własność 

Visible

. Pierwszym wspólnym przodkiem wizualnych sterowników 

Delphi określającym własność 

Visible

 jest klasa komponentu 

TControl

Można się o tym przekonać przeglądając hierarchię klas Delphi. Z dwóch metod 
stosowanych do tego celu łatwiejsza polega na skorzystaniu z przeglądarki 
obiektów (Object Browser). Narzędzie, które jest dostępne po pierwszym 
skompilowaniu projektu, uruchamiamy wybierając opcję 

Browser

 z menu 

View

 

głównego menu Delphi. Szukamy interesujących nas obiektów albo poprzez 
przewijanie struktury klas w 

lewym oknie przeglądarki, albo korzystając 

z możliwości wyszukiwania według nazwy. Po przewinięciu lewego okna do 
miejsca umożliwiającego obserwację komponentu 

Tbutton

, możemy na 

przykład odnotować,  że dziedziczy własności bezpośrednio z 

klasy 

TButtonControl

, natomiast w 

drugiej kolejności (jest „wnuczkiem”) 

TWinControl

. Ta z kolei jest bezpośrednio klasą potomną 

TControl

Przeglądając prawe okno możemy znaleźć  własność 

Visible

 obiektów klasy 

TControl

Inny sposób wyśledzenia miejsca w hierarchii interesującej nas klasy polega na 
analizie kodu źródłowego VCL. Chociaż jest on trudniejszy od poprzedniego, to 
w trakcie realizacji można się wiele nauczyć i uzyskać dużo więcej informacji. 
Rozpocząć należy od modułu, który definiuje klasę i podążać wstecz. Do 
lokalizacji modułów, określających konkretne komponenty, przydaje się efektywne 
narzędzie wyszukujące na podstawie tekstu. Na szczęście nomenklatura modułów 
stosowana przez firmę Borland jest bardzo poręczna, więc praca nie jest tak 
trudna, jak może się wydawać. Aby wyszukać odpowiedniego rodzica, należy 
zwracać uwagę na definicję typu każdej klasy. Informacja zawarta jest w pierwszej 
linii każdej klasy. Na przykład pierwszy wiersz definicji klasy Tbutton jest 
następujący: 

TButton = class( TButtoControl ) 

Oznacza to, że 

Tbutton

 dziedziczy własności klasy 

TButtonControl

Następnie w definicji klasy 

TButtonControl

 znajdujemy: 

TButtonControl = class (TWinControl ) 

Klasą rodzicielską dla 

TButtonControl

 jest zatem 

TWinControl

. Zarówno 

Tbutton

, jak i 

TButtonControl

  są zdefiniowane w module 

Stdctrls

Uważna analiza obu definicji dowodzi, że  żadna z nich nie określa własności 

Visible

, co oznacza, że wędrówkę trzeba kontynuować dalej. 

TWinControl

 

jest zdefiniowana w module 

Controls

. Odpowiednia linia kodu wygląda 

następująco: 

TWinControl = class ( TControl )

 

background image

 Raporty 

391

 

Jak się przekonamy, również ta klasa nie określa własności 

Visible

. Dopiero 

w jej klasie rodzicielskiej, to znaczy w 

TControl

, znajdziemy interesującą nas 

definicję. Wyjaśnia to, dlaczego wykorzystaliśmy klasę 

Tcontrol

 do typowania 

komponentów formularza wymagających zmiany własności 

Visible

UWAGA 

Metoda rzutowania, zastosowana w kodzie do drukowania formularza, nosi nazwę 
rzutowania kontrolowanego (Checked typecasts). Sprawdzenie odbywa się 
w czasie pracy programu. Jeśli rzutujemy obiekt klasy - używając typu klasy, który 
nie jest typem klasy naszego obiektu, ani żadnym typem jego klas rodzicielskich, 
zostanie wygenerowany wyjątek. 

Odmienną metodą jest rzutowanie niekontrolowane, które można zaimplemen-
tować w następujący sposób: 

With TControl ( Components [ i ] ) do

 

Przy takim sposobie rzutowania, jeśli pomylimy obiekt klasy, to najczęstszym 
skutkiem będzie wygenerowanie próby zabronionego dostępu i aplikacja zostanie 
„zabita” przez system. Rzutowanie kontrolowane jest zarówno funkcjonalne, jak 
i bezpieczne; należy je stosować zawsze, kiedy to jest możliwe. 

Innym interesującym aspektem kodu do drukowania formularza jest brak na liście 
komponentów przeznaczonych do rzutowania klasy 

TBitBtn

. Elementy 

wymienionej klasy nie powinny być drukowane, a wcześniej na formularzu 
umieściliśmy przycisk 

bbPrint

. Sprawdźmy, w jaki sposób następujący kod 

traktuje komponenty klasy 

TBitBtn

If ( Components [ I ] is Tbutton ) or 
( Components [ I ] is TDBNavigator ) or 
( Components [ I ] is TSpeedButton ) then 

Odpowiedź ukryta jest w hierarchii klas. RTTI jest operatorem, który zwraca 
wartość True, jeśli sprawdza obiekt używając własnej klasy obiektu lub 
którejkolwiek z klas rodzicielskich
. Ostatnie zdanie jest bardzo ważne. Ponieważ 

TBitBtn

 dziedziczy z klasy 

Tbutton

, to analiza tego typu obejmuje również 

TBitBtn

. Niestety, 

TSpeedButton

 nie jest typem potomnym 

Tbutton

 

dlatego musi być sprawdzany oddzielnie. 

Droga na skróty? 

Można postawić pytanie, czy zamiast analizować wszystkie komponenty 
formularza, nie lepiej po prostu ukryć  ręcznie te z nich, które nie powinny być 
widoczne. Podobnie można zaprojektować formularz zajmujący pełny ekran, 

background image

392 

Część II 

pozbywając się problemu jego rozmiaru przy drukowaniu w czasie działania 
programu. Można przykładowo napisać krótką procedurę: 

DBNavigator1.Visible:=False; 
bbOK.Visible:=False;  
bbCancel.Visible:=False;  
bbPrint.Visible:=False;  

Napisany kod jest z pewnością krótszy od poprzedniego, ale więcej jest 
problemów z nim związanych . Po pierwsze, w przeciwieństwie do wcześniejszego 
rozwiązania, dodanie do formularza nawet jednego dodatkowego przycisku będzie 
wymagać zaktualizowania procedury. Po drugie, dodanie jakiegokolwiek 
komponentu, który nie powinien być drukowany (na przykład przycisku), do 
jakiegokolwiek formularza rodzicielskiego będzie powodowało ten sam problem. 
Po trzecie, powyższy styl programowania nie pozwala, aby wybrany komponent 
mógł być wcześniej ukryty. Dokładniej, jeśli w rozważanej procedurze zechcemy 
uwzględnić możliwość ukrywania przycisków w czasie inicjowania formularza, to 
przed każdą linią dotyczącą konkretnego komponentu należałoby umieścić 
polecenie, zachowujące stan jego własności 

Visible

 przed dokonaniem zmiany. 

Należałoby również zaprogramować odtwarzanie własności 

Visible

 po 

wydrukowaniu raportu. Wkrótce otrzymamy co najmniej tyle samo linii programu, 
co wcześniej, mając w zamian rozwiązanie dużo mniej elastyczne. Powiększanie 
formularza może być pożyteczne z punktu widzenia potrzeby drukowania, ale 
niekoniecznie ze względu na wygląd formularza na monitorze. Z różnych 
powodów możemy nie chcieć, aby formularz zajmował nam cały ekran. W takim 
przypadku konieczność zapewnienia dobrego wydruku nie powinna ograniczać 
naszej swobody projektowania. 

Zawarty w listingu 12.1 kod metody 

OnClick

 przycisku 

bbPrint

 może być 

wykorzystany we wszystkich aplikacjach Delphi oraz we wszystkich formularzach. 
Możemy stosować to rozwiązanie w następnych, własnych aplikacjach. Można 
nawet skopiować przycisk i jego program 

OnClick

 do nadrzędnej klasy 

formularzy - 

fmAnyForm

, dodając w ten sposób zdolność drukowania do 

wszystkich formularzy systemu RENTMAN. 

Drukowanie wielu rekordów 

Bardzo  łatwo zaprogramować drukowanie wielu wierszy tabeli. Aby móc 
drukować wszystkie zlecenia w pliku, wystarczy napisać procedurę: 

With taWorder do begin 
First; 
While not (EOF) do begin 
fmRWORMDE0.bbPrintClick (Self); 
Next; 
end; 

background image

 Raporty 

393

 

end; 

Formularze drukowania 

Zapamiętajmy,  że  łatwo jest zaprojektować formularze, których jedynym 
przeznaczeniem będzie wydrukowanie. Możemy, na przykład, skonstruować raport 
dziedziczący własności formularza kombinowanego work-order, przeznaczony 
wyłącznie do drukowania. Możemy, stosując różne elementy, ukształtować 
zestawienie w sposób podnoszący jego czytelność, dobierając w szczególności 
dobrze wyglądające na papierze zestawienia kolorów (niektóre popularne 
kombinacje, które sprawdzają się na ekranie, takie jak czarny tekst na szarym tle, 
powinny być zabronione), wykorzystując różne style ramek itp. Już w czasie 
projektowania ukrywamy komponenty, które nie powinny być widoczne na 
wydruku, nie martwiąc się, jak to zrobić w trakcie pracy programu. W tym 
rozdziale nie planujemy budowy formularzy przeznaczonych wyłącznie do 
drukowania, ale można rozważyć samodzielne wykonanie takiego projektu. 

UWAGA 

Tworzenie raportów poprzez drukowanie specjalnych formularzy jest najmniej 
efektywnym sposobem wyprowadzania danych w postaci wydruku. Obraz 
formularza przesyłany do wydruku jest dużą mapą bitową, co wymaga od drukarki 
posiadania dużej ilości pamięci. Niektóre urządzenia mogą nie być w stanie 
wydrukować dokumentu w całości. Drukowanie formularzy tą metodą trwa 
również  dłużej, niż podobnych raportów przy użyciu innych metod. Należy 
również uwzględnić, że ponieważ wydruk jest w zasadzie odwzorowaniem obrazu 
na ekranie, to na jego jakość ma wpływ relatywnie niska rozdzielczość monitora 
oraz stosunkowo wysoka rozdzielczość drukarki. 

Alternatywne metody tworzenia raportów formularzowych 

Raporty mające kształt formularzy mogą być również projektowane przy użyciu 
komponentów programu narzędziowego Delphi- QuickReport lub komercyjnych 
pakietów do tworzenia raportów. Wielu użytkowników może uznać, że korzystanie 
z tych  narzędzi jest łatwiejsze niż drukowanie formularzy Delphi 
wykorzystywanych na monitorze. Dla prostych raportów tego typu, 
przeznaczonych do wewnętrznego użytku, zastosowanie metody 

Print

 obiektu 

TForm

, wydaje się zupełnie wystarczające, jednak rodzina komponentów 

QuickReport

 zawiera narzędzia, mogące zaspokoić zarówno proste, jak 

i bardziej wyrafinowane potrzeby. 

background image

394 

Część II 

Testowanie raportu w postaci formularza 

Jesteśmy gotowi do sprawdzenia nowego raportu. Zachowujemy projekt 
i uruchamiamy  aplikację. Wybieramy opcję 

Reports\Work Order

 (lub wciskamy 

klawisz F7). Na ekranie powinien ukazać się formularz do wyboru wykazu prac. 
Wybieramy jakieś zestawienie i wciskamy przycisk 

Edit

. Następnie klikamy 

przycisk 

Print

. Powinniśmy ujrzeć formularz powiększony do pełnego ekranu 

z ukrytymi przyciskami sterowników. Po wydrukowaniu zestawienie powinno 
wrócić do normalnego wyglądu. Gdy zakończymy próby, zamykamy program 
i wracamy do Delphi. 

UWAGA 

Zwróćmy uwagę na możliwość wykorzystania właściwości 

PrintScale

 obiektu 

Tform

 do sterowania sposobem, w jaki formularz jest obsługiwany przez swoją 

metodę 

Print

. Domyślne ustawienie ma wartość 

poProportional

, co 

powoduje drukowanie formularza bardzo podobnego do swojego odbicia na 
ekranie. Zachowana jest ta sama rozdzielczość wyrażona w liczbie pikseli na jeden 
cal. Ustawienie wartości 

poPrintToFit

 skutkuje wydrukiem formularza, 

z zachowaniem odpowiednich proporcji, ale z wymiarami zmienionymi, by 
dopasować go do strony wydruku. Wreszcie ustawienie 

poNone

, które wyłącza 

wszelkie specjalne zasady skalowania i wymiarowania. Drukowanie formularza 
różniącego się zasadniczo od swojego odpowiednika ekranowego może dać dobre 
wyniki, gdyż rozdzielczość współczesnych monitorów nie nadąża za parametrami 
większości drukarek. 

Kolejny raport skonstruujemy wykorzystując komponenty programu Quickreport. 
Przekonamy się,  że korzystanie z tego narzędzia jest tylko trochę bardziej 
skomplikowane od stosowania standardowych elementów ekranowych. 
Komponenty programu nadają się do konstruowania wielu rodzajów raportów, 
a narzędzia QuicReport zawierają szybkie procedury nadające się do 
konstruowania profesjonalnych raportów w komercyjnych aplikacjach tworzonych 
przy użyciu Delphi. 

Zestawienie kolumnowe- Lista nieruchomości 

Następnym raportem, który zbudujemy, będzie lista elementów tabeli 
PROPERTY. Do szybkiej konstrukcji zestawienia wykorzystamy nowy kreator 
Delphi- Quickreport. 

Wybieramy opcję 

File\New

, a następnie pozycję 

Busines

 w oknie dialogowym 

New

 

Item

. Aby rozpocząć pracę z kreatorem, dwukrotnie klikamy 

Quickreport

  Wizard 

background image

 Raporty 

395

 

Keator będzie nam zadawał kilka pytań i konstruował podstawowy raport 
w oparciu o udzielone odpowiedzi. 

Najpierw zostaniemy poproszeni o wskazanie katalogu lub aliasu bazy danych, 
z którą będziemy pracować. Należy znaleźć i wybrać na liście alias 

dbRENTMAN

Na dole ekranu pojawi się zapytanie o tabelę, która jest podstawą raportu. 
Wybieramy z listy tabelę PROPERTY i klikamy na przycisku 

Next

Następnie będziemy proszeni o wskazania pól tabeli, które mają być uwidocznione 
na zestawieniu. Wybieramy: PROPERTY_NUMBER, ADDRESS, CITY, STATE, 
ZIP, ADDITION, I SCHOOLDISTRICT. Możemy przemieszczać pola między 
listami 

Avaliable

 (dostępne) i 

Selected

 (wybrane) - poprzez dwukrotne kliknięcie 

odpowiedniej pozycji lub dwuetapowo: najpierw zaznaczając element, a następnie 
wciskając odpowiedni przycisk ze strzałką. Po wytypowaniu wszystkich pól, 
należy kliknąć przycisk 

Next

Ostatnie pytanie dotyczy tytułu raportu. Należy wpisać odpowiednią nazwę, na 
przykład „Lista nieruchomości”, a następnie zdecydować się na odpowiedni krój 
czcionki, jaką będzie napisana ( Arial wydaje się dobrym wyborem). 

Kończymy pracę wciskając przycisk 

Finish

, a kreator wygeneruje podstawowy 

raport, który można zmodyfikować według swojego uznania. 

Pierwsze ujęcie zestawienia, chociaż mało skomplikowane, wymaga z pewnością 
pewnych modyfikacji. Przełączamy na 

True

  własność 

Active

 komponentu 

Table

 i wybieramy 

Preview -

 aby ujrzeć, jak będzie wyglądał raport w czasie 

pracy programu. Rysunek 12.1. przedstawia obraz, który powinniśmy zobaczyć. 

Zmodyfikowane zestawienie narzędzi podnoszących czytelność i estetykę raportu 
ilustruje rysunek 12.2. 

Oto kilka najważniejszych zmian wprowadzonych w sprawozdaniu: 

„Podsumowania kolumn - zauważmy umieszczoną na dole raportu liczbę 

wszystkich nieruchomości. QuickReport umożliwia grupowanie, sortowanie 
wszystkich grup itp. 

„Kolory obiektów i pasków - w przykładzie wykorzystano kolory pasków, aby 

nieco skontrastować szczegóły. 

„Pola systemowe - zwróćmy uwagę na datę i czas sporządzenia raportu 

umieszczone w lewym górnym rogu zestawienia. Pola zostały utworzone przy 
użyciu komponentu 

QRSysData

 programu Quickreport. 

background image

396 

Część II 

 

WSKAZÓWKA 

Służący do tworzenia wyrażeń komponent 

QRExpr

 programu QuickReport nie 

umożliwia korzystania ze składni ANSI SQL do wyprowadzania danych 
sumarycznych. Aby uzyskać liczbę rekordów tabeli, nie należy używać wyrażenia 

COUNT(*).

 Nie działa również 

COUNT(PROPERTY_ NUMBERS)

QRExpr

 

dopuszcza wyłącznie słowo 

COUNT

, bez nawiasów i na pewno nie zezwala na 

stosowanie składni ANSI SQL. 

 

Rysunek 12.1. 
Pierwsze ujęcie 
zestawienia 
nieruchomości, 
utworzonego przy 
pomocy kreatora 
QuickReport. 

 

Rysunek 12.2. 
To samo 
sprawozdanie po 
małej kosmetyce. 

background image

 Raporty 

397

 

 

UWAGA 

Dołączony do książki CD-ROM zawiera kody źródłowe wszystkich raportów 
omawianych w tym rozdziale.  

Po zakończeniu przekształceń zestawienia utworzonego przez kreator, należy 
nadać mu nazwę frRPROLST0 i zapisać na dysku jako 

RPROLST0.PAS

Połączenie raportu z aplikacją 

Aby w pełni sprawdzić działanie raportu, musimy połączyć go z aplikacją 
RENTMAN. W tym celu powinniśmy: 

„Zadeklarować moduł formularza raportu w 

głównym module aplikacji, 

RSYSMAN0. 

„Dodać odpowiednią pozycję do menu oraz odpowiednio zaprogramować 

wywołanie nowego raportu w głównym menu programu. 

Realizację powyższych zamierzeń rozpoczniemy od otwarcia 

fmRSYSMAN

 

w projektancie formularzy. Wybieramy opcję 

Use

 

Unit

 w menu 

File

 i dwukrotnie 

klikamy na pozycji RPROLST0. Dzięki temu możemy odwoływać się do 
formularza raportu zawartego w module RPROLST0. 

Kolejno: klikamy dwukrotnie komponent 

MainMenu

 i wybieramy czystą pozycję 

w menu 

Reports

. Wprowadzamy nagłówek: &Property List i określamy klawisz 

skrótu: F11. Dwukrotnie klikamy na modyfikowanej pozycji menu i przy pomocy 
edytora kodu piszemy: 

frRPROLST0.QuickRep1.Preview;

 

Dzięki temu poleceniu nowy raport będzie wyświetlany na ekranie w momencie 
drukowania, zachowywania lub unieważniania. 

Po wykonaniu wszystkich wymienionych czynności jesteśmy gotowi do 
sprawdzenia raportu podczas pracy programu. Zachowujemy projekt 
i uruchamiamy aplikację. Rysunek 12.3. ilustruje wygląd raportu podczas działania 
programu. 

background image

398 

Część II 

Raport przedstawiający zestawienie zadań 

Ostatni raport w tym rozdziale zostanie skonstruowany wyłącznie przy użyciu 
języka Object Pascal. Jeśli kiedykolwiek bowiem spotkamy się z raportem 
wymagającym specjalnych środków, niemożliwych do osiągnięcia przy użyciu 
komponentów 

QuickReport

, zawsze możemy się uciec do ręcznego 

zaprogramowania raportu, używając do tego wyłącznie kodu Object Pascal-a. 
Ponieważ Borland Pascal został zaprojektowany jako język programowania 
ogólnego przeznaczenia, posiada wszystkie narzędzia, których można oczekiwać 
od języka programowania trzeciej generacji takiego jak C, Basic czy Pascal. 
Object Pascal zaimplementowany w Delphi jest dostatecznie silnym narzędziem, 
by przy jego pomocy utworzyć raport na miarę każdych oczekiwań. 

Umieszczamy nowy komponent 

Query

 w 

module danych 

dmRENTMAN

Nadajemy mu nazwę 

quTaskList

 i wprowadzamy następujący kod SQL: 

SELECT E.Name, P.Address, P.City, P.Addition, T.Description, 

 T.TaskDuration 

FROM EMPLOYEE E, 
WORDER W, 
PROPERTY P, 
WODETAIL D, 
WORKTYPE T, 
WHERE E.EMPLOYEE_NUMBER=W.EMPLOYEE_NUMBER 
and W.WORDER_NUMBER=D.WORDER_NUMBER 
and W.PROPERTY_NUMBER=P.PROPERTY_NUMBER 
and D.WORK_TYPE_CODE=T.WORK_TYPE_CODE 
and W.StartDate<=:ListDate 

 

Rysunek 12.3. 
Raport 
przedstawiający 
zestawienie 
nieruchomości- 
podczas pracy 
programu. 

background image

 Raporty 

399

 

and W.EndDate>=: ListDate 
ORDER BY E.Name, P.Address, P.City, P.Addition, T.Description 

Powyższe zapytanie będzie  łączyć tabele EMPLOYEE, WORDER, PROPERTY, 
WODETAIL i 

WORKTYPE - celem stworzenia poszerzonej listy zadań 

związanych z określoną datą. Zauważmy,  że zapytanie definiuje pojedynczy 
parametr :

ListDate

, który musi być określony, aby procedura działała 

prawidłowo. Wykorzystując edytor właściwości do skonfigurowania własności 

Params

 komponentu 

Query

, ustawiamy typ parametru 

ListDate

 na 

Date

Następnie określamy 

DatabaseName

 jako 

dbRENTMAN

, dwukrotnie  klikamy 

komponent i dodajemy wszystkie jego pola jako elementy 

TField

W tym momencie jesteśmy gotowi do napisania kodu służącego do utworzenia 
raportu. Do projektanta formularzy wczytujemy główny formularz aplikacji - 
fmRSYSMAN0 i dwukrotnie klikamy na komponencie 

MainMenu

, aby otworzyć 

narzędzie do projektowania menu. Wybieramy opcję 

Reports\Task List

 

i ustawiamy klawisz skrótu na F10. Dwukrotnie klikamy na opcji 

Task

 

List

 

i zmieniamy obsługę zdarzenia 

OnClick

 zgodnie z listingiem 12.2. 

Listing 12.2. Procedura obsługi zdarzenia TaskList1Click. 

 

procedure TfmRSYSMAN0.TaskList1Click(Sender: TObject); 
var 
 

LastName : String; 

 

CurrentLine : Byte; 

 procedure 

PrintColumnHeadings; 

 begin 
 

  Writeln(PrintFile,' '+Pad('Address',30),  

 

 

 Pad('City',20), Pad('Addition',20),Pad('Work',30), 

 

 

 Pad('Time (Days)',15)); 

   end; 

begin 
 inherited; 
 

If (MessageDlg('Print the Task List report?',  

 

 

 mtConfirmation,mbYesNoCancel,0)<>mrYes) then Exit; 

 try 
 Cursor:=crHourGlass; 
 

With dmRENTMAN, quTaskList do begin 

  

ParamByName('ListDate').AsDate:=StrToDate(‘04/05/97’); 

 

  //Supply a valid date here based on your tests data 

//  ParamByName('ListDate').AsDate:=Date; 
  

Open; 

  

try 

  

BeginReport(poPortrait,Caption); 

  

try 

 

  While not eof do begin 

  

 

CurrentLine:=9; 

background image

400 

Część II 

 

   

PrintHeader('RTSKLST0',Caption,'Employee Task  

 

   

 List',ParamByName('ListDate').AsString); 

  

 

PrintColumnHeadings; 

 

   

While (not eof) and (CurrentLine<>PageLength) do  

  

 

begin 

 

 

  Writeln(PrintFile); 

 

 

  Writeln(PrintFile,'EMPLOYEE:'+quTaskListName. 

 

   

 

 AsString); 

 

 

  Inc(CurrentLine,2); 

 

 

  Repeat 

 

 

   LastName:=quTaskListName.AsString; 

 

 

   Writeln(PrintFile,' 

 

'+Pad(quTaskListAddress. 

 

 

 

   AsString,30),Pad(quTaskListCity.AsString,20), 

  

 

Pad(quTaskListAddition.AsString,20), 

  

 

Pad(quTaskListDescription.AsString,30), 

 

   

 Pad(quTaskListTaskDuration.AsString,15)); 

 

 

  Next; 

 

 

  Inc(CurrentLine); 

 

   

 

Until (eof) or (CurrentLine=PageLength) or 

 

 

  (quTaskListName.AsString<>LastName); 

  

 

end; 

  

end; 

  

finally 

  

 

EndReport; 

  

end; 

  

finally 

  

 

quTaskList.Close; 

  

end; 

 end; 
 finally 
 Cursor:=crDefault; 
 

MessageDlg(‘Task List Report Finished’,mtInformation,  

 

 [mbOK],0); 

 end; 
end; 

Elementy napisanego wyżej programu wymagają wielu innych funkcji i procedur. 
W module 

RSYSMAN0

, bezpośrednio przed procedurą 

TaskList1.Click

należy wpisać kod zawarty w listingu 12.3. 

Listing 12.3. Funkcje narzędziowe potrzebne do raportu Task 

List.  

function Pad(InStr : String; TotalLen : Integer) : String; 
begin 
 Result:=InStr; 
 

While (Length(Result)<TotalLen) do Result:=Result+' '; 

end; 

function LPad(InStr : String; TotalLen : Integer) : String; 
begin 

background image

 Raporty 

401

 

 Result:=InStr; 
 

While (Length(Result)<TotalLen) do Result:=' '+Result; 

end; 

function Center(InStr : String; TotalLen : Integer) : String; 
var 
 

NumSpace : Integer; 

 

Temp : String; 

begin 
 

NumSpace := (TotalLen-Length(InStr)) div 2; 

 Temp:=''; 
 

While Length(Temp)<NumSpace do Temp:=Temp+' '; 

 Result:=Temp+InStr+Temp; 
 

While Length(Result)<TotalLen do Result:=Result+' '; 

end; 

procedure PrintHeader(ReportName,SystemTitle,ReportTitle, 

 Criteria : String); 

begin 
 

If (Printer.PageNumber <> 1) then Write(PrintFile,^L); 

 Writeln(PrintFile,Pad('Report: 

'+ReportName,25), 

 

 Center(SystemTitle,LineLength-45),LPad('Page:  

 

 '+IntToStr(Printer.PageNumber),25)); 

 Writeln(PrintFile); 
 

Writeln(PrintFile,Pad('Print date: '+DateToStr(Date),25),  

 

 Center(ReportTitle,LineLength-45),LPad('Print time:  

 

 '+TimeToStr(Time),25)); 

 Writeln(PrintFile); 
 

Writeln(PrintFile,Pad('User name: '+UserName,25), 

 

 Center('For: '+Criteria,LineLength-45)); 

 Writeln(PrintFile); 
end; 

procedure BeginReport(Orientation : TPrinterOrientation;  

 Title :String); 

begin 
 Printer.Orientation:=Orientation; 
 Printer.Title:=Title; 
 AssignPrn(PrintFile); 
 Rewrite(PrintFile); 
 

With Printer.Canvas.Font do begin 

  

 

Name:='Courier 

New'; 

  

 

Height:=-13; 

  

 

Style:=[]; 

 end; 
 

LineLength:=(Printer.PageWidth div Printer.Canvas.  

 

 TextWidth('X'))-5; 

background image

402 

Część II 

 

PageLength:=(Printer.PageHeight div Printer.Canvas. 

 

 TextHeight('X'))-2; 

end; 

procedure EndReport; 
begin 
 CloseFile(PrintFile); 
end; 

Napisane wyżej funkcje i procedury korzystają ze zmiennych, które powinny być 
zadeklarowane jako globalne w module RSYSMAN0. W bloku deklaracji var, 
bezpośrednio przed linią 

{$R *.DFM}

, należy umieścić odpowiednie zapowiedzi 

zmiennych np.: 

var 
PrintFile: Text; 
LineLength: Integer; 
PageLength: Integer; 
UserName: String; 

Musimy również uwzględnić,  że napisany przez nas program wymaga użycia 
modułu 

Printers

. Zawarty w Delphi moduł 

Printers

 pozwala wykorzystać 

podstawowy interface drukowania systemu Windows. Dodajemy zatem deklarację 

Printers

 do linii deklaracji modułów 

Uses

... 

Działanie metody TaskList1Click 

Kilka punktów procedury 

TaskList1Click

 zasługuje na dyskusję. Zauważmy 

najpierw wykorzystanie standardowej procedury 

Writeln

 do wysyłania danych 

na wyjście drukarki. Poprzez zastosowanie procedury 

AssignPrn

 odbywa się 

skojarzenie pliku tekstowego z domyślnym urządzeniem drukującym Windows. 
Otwierając i dopisując dane do pliku, powodujemy wysyłanie jego zawartości na 
wyjście drukarki Windows. 

Istnieją dwa sposoby drukowania tekstów przez aplikacje Windows. 

Pierwszy utrzymuje, że do rozpoczęcia procesu drukowania (w celu wysłania 
tekstu do drukarki) należy używać procedury 

BeginDoc

 i 

procedury 

TextOutput

. Wiele przemawia za tą metodą,  a w szczególności możliwość 

całkowitej kontroli nad wyglądem tekstu oraz zdolność do umieszczania go 
dokładnie w zaplanowanym miejscu. Chociaż z drugiej strony płaci się za to 
skomplikowanym sposobem konfiguracji i wykorzystania. Jeśli zachodzi potrzeba 
tak dokładnego sterowania wydrukiem, lepiej chyba skorzystać z kompleksowych 
rozwiązań udostępnianych przez specjalne narzędzia do tworzenia raportów. Są 

background image

 Raporty 

403

 

one zazwyczaj wyposażone w szeroką gamę środków do sterowania wyjściem na 
drukarkę.  

Drugi zaleca drukowanie tekstów w programach pod kontrolą Windows metodą 
zastosowaną przez nas. Pomysł polega na stosowaniu środków najprostszych 
spośród możliwych. Zadania wymagające precyzyjnie zaplanowanego wydruku 
można bowiem wykonać przy pomocy specjalnych narzędzi do tworzenia 
raportów. Używając do drukowania standardowych procedur obsługi wyjścia 
korzystamy, bez zbędnych komplikacji, ze wszystkich ich udogodnień. Takie 
proste podejście pozwala pisać proste i niedługie procedury. 

Procedura BeginReport 

Zbadamy teraz szczegółowo wszystkie części napisanego programu, zaczynając od 
procedury 

BeginReport

. Dla przypomnienia odpowiedni tekst kodu wygląda 

następująco: 

procedure BeginReport 

 (Orientation : TPrinterOrientation; Title :String); 

begin 
 Printer.Orientation:=Orientation; 
 Printer.Title:=Title; 
 AssignPrn(printFile); 
 Rewrite(PrintFile); 
 

With Printer.Canvas.Font do begin 

  Name:=‘Courier 

New’; 

  Height:=10; 
  Style:=[]; 
 end; 
 

LineLength:=(Printer.PageWidth div Printer.Canvas. 

 

 TextWidth(‘X’))-5; 

 

PageLength:=Printer.PageHeigth div Printer.Canvas. 

 

 TextHeight(‘X’))-2 

end; 

Zauważmy,  że orientacja wydruku raportu jest przekazywana do procedury 
poprzez zmienną typu 

TPrinterOrientation

. Powyższy typ jest 

zdefiniowany w module 

Printers

 i ma dwie możliwe wartości 

poPortrait

 

poLandscape

. Ponieważ orientacja strony jest zwykle zależna od procedury, 

która ją wywołuje, to w chwili inicjowania zadania wydruku powinna być 
określona.  

Sekcja 

With Printer.Canvas.Font

 konfiguruje fonty używane do 

drukowania. Zakładamy,  że cały raport będzie drukowany tym samym krojem. 
Ponadto przyjmujemy, że będziemy używać nieproporcjonalnego kroju True Type, 

background image

404 

Część II 

mianowicie Courier New. Na ogół nie zaleca się stosowania fontów 

proporcjonalnych.  

Zwróćmy uwagę na sposób kalkulowania szerokości i wysokości strony. Linia: 

LineLength:=(Printer.PageWidth div Printer.Canvas.  

 TextWidth(‘X’))-5;

 

dzieli liczbę pikseli na drukowanej stronie (obliczoną na podstawie aktualnego 
rozmiaru papieru i orientacji strony) przez szerokość pojedynczego znaku „X” 
aktualnie stosowanego fontu. Jest to jeszcze jeden powód, aby korzystać 
z czcionek nieproporcjonalnych. Ponieważ wszystkie znaki mają w tym przypadku 
tę samą szerokość, to kalkulacja wykorzystująca funkcję 

TextWith

  będzie 

poprawna. W 

przypadku korzystania z 

czcionek proporcjonalnych proces 

obliczania maksymalnej liczby znaków w jednej linii jest dużo bardziej złożony. 
Od ilorazu powinno się odjąć liczbę pięć, aby umożliwić zastosowanie lewego 
i prawego marginesu. 

Przekazywany do procedury parametr 

Title

 jest wykorzystywany do ustawiania 

nazwy (używanej w 

buforze drukarki Windows i 

na sieciowych stronach 

nagłówkowych) zadania przeznaczonego do drukowania. Jeśli konfiguracja sieci 
wymaga drukowania strony nagłówkowej przed każdym wydrukiem, to można 
użyć  własności 

Title

 z 

TPrinter

 do ustawienia tekstu, który ma tam zostać 

wydrukowany. 

Funkcje 

Pad

Lpad

 oraz 

Center

 pełnią podobne zadanie. Jedna 

z podstawowych  trudności związanych z formatowaniem wydruku polega na 
odpowiednim ustawieniu szerokości kolumn, zawierających dane o 

różnej 

długości. Drugi problem to równomierne rozmieszczenie nagłówków kolumn 
i danych, które je opisują. Jeśli nagłówek kolumny jest dłuższy od danych 
zawartych w rubryce, to jej szerokość musi być dopasowana do nagłówka. Jeśli 
dane są szersze od nagłówka, to tytuł kolumny musi być uzupełniony 
o odpowiednią liczbę spacji. Wymienione wyżej funkcje mają za zadanie 
dopełnianie rozmiaru elementów raportu w sposób ułatwiający wyrównanie 
kolumn. Obok decyzji o użyciu fontów nieproporcjonalnych, logiczne dopełnianie 
rozmiaru elementów kolumn jest najważniejszym aspektem osiągnięcia 
poprawnych wydruków. 

Procedura PrintHeader 

Procedura 

PrintHeader

 została napisana modułowo, by nadawała się do 

wykorzystania w innych programach. Chociaż drukowanie raportów wyłącznie 
przy użyciu kodu programu nie jest metodą godną rekomendacji, to w razie 
potrzeby można wykorzystać fragmenty programu napisane w tym rozdziale, 
a w szczególności procedurę 

PrintHeader

. Otrzymane jako parametry 

background image

 Raporty 

405

 

elementy: 

ReportName

SystemTitle

ReportTitle

 i 

Criteria

procedura drukuje w nagłówku strony raportu. Taka elastyczność umożliwia 
wykorzystanie kodu praktycznie we wszystkich rodzajach raportów. 

Linia: 

If ( Printer.PageNumber <> 1 ) then Write ( PrintFile,#12 ); 

powoduje sprawdzenie, czy procedura 

PrintHeader

 wywoływana jest po raz 

pierwszy. Jeśli nie, to każdorazowo na drukarkę zostanie wysłany znak CTRL+L. 

Znak ASCII o kodzie #12 jest praktycznie na każdej drukarce interpretowany jako 
polecenie wysunięcia strony. Nie należy oczekiwać wysunięcia strony papieru przy 
pierwszym wywołaniu procedury ani zapominać o uzupełnieniu papieru przed 
drukowaniem raportu. 

Funkcje „zwracające” datę i czas systemowy są aktualizowane przy każdym 
wywołaniu. W czasie przepisywania kodu warto rozważyć możliwość zachowania 
daty i czasu określonych w chwili rozpoczęcia wydruku i drukowanie w raporcie 
zapamiętanych wartości. 

Celem kalkulacji opartej o długość strony w wywołaniu funkcji 

Center

 dla tytułu 

systemu, tytułu raportu i kryteriów pól jest umożliwienie zmiany orientacji strony. 
Jeśli orientacja zostanie przełączona na 

poPortrait

, to nagłówek zostanie 

dynamicznie przemieszczony i prawidłowo wydrukowany. 

Więcej o procedurze TaskList1Click 

Linia: 

ParamByName (‘ListDate’).AsDate:=Date 

dostarcza aktualną datę systemową do zapytania 

quTaskList

 jako jego parametr 

ListDate

. W napisanym programie polecenie zaznaczone jest jako komentarz. 

Uaktywnienie go umożliwia drukowanie raportu zaopatrzonego w aktualną datę 
(należy oczywiście zmienić na komentarz wcześniejsze polecenie podstawienia 
stałej daty). Po wyznaczeniu parametru 

ListDate

 wywoływana jest metoda 

Open

, która ma za zadanie zainicjowanie zapytania i dostarczenie programowi 

jego wyników. 

Bloki postaci 

try...finally

 wykorzystywane są do oprogramowania obsługi 

wyjątków. W programie zastosowano trzy konstrukcje odpowiedzialne za reakcje 
na problemy mogące wyniknąć w czasie działania procedury. Pierwsza odpowiada 
za zamknięcie zapytania przez zakończeniem procedury, druga ma za zadanie 
czuwać, aby wykorzystywany do drukowania plik tekstowy został zamknięty przed 
zakończeniem pracy procedury bez względu na to, czy kończona jest normalnie, 

background image

406 

Część II 

czy wskutek błędu. Celem trzeciej jest przywrócenie wskaźnikowi kursora 
normalnej postaci po zakończeniu wydruku. 

Sekcja 

Repeat... Until

  (

quTaskListName.AsString<>LastName

dotyczy grupowania wydruków raportu. Pomysł polega na drukowaniu 
oddzielnych list zadań dla każdego pracownika. Dzięki zastosowaniu konstrukcji 
SQL: 

ORDER BY

 oraz logice pętli 

Repeat

 ... 

Until

 zadanie jest wykonywane 

prawidłowo: listy poleceń dla każdego pracownika drukowane są jedna po drugiej. 

Wskaźnik myszy jest zamieniany na klepsydrę bezpośrednio przed rozpoczęciem 
wydruku, a po zakończeniu przywracany do normalnej postaci. Ważne jest 
zasygnalizowanie,  że po uruchomieniu raportu coś zaczęło się dziać; im więcej 
możliwości kontroli nad programem ma użytkownik, tym lepiej. W tym samym 
celu można rozważyć inne rozwiązanie, polegające na wykorzystaniu do 
ilustrowania postępu w 

drukowaniu komponentu Delphi o 

nazwie 

TProgressBar

. Do aktualizacji wskaźnika można wykorzystać moment 

zwracania wartości przez zapytanie.  

Podgląd wydruku raportu 

Innym udogodnieniem jest możliwość przejrzenia raportu przed wydrukowaniem. 
Można to łatwo uzyskać przez wskazanie wyjścia przypisaniem 

Assign

 zamiast 

AssignPrn

. Raport jest plikiem tekstowym, do przeglądu można zatem 

wykorzystać metodę 

LoadFromFile

 komponentu 

Memo

, która pozwala na 

wyświetlenie pliku w oknie wyposażonym w możliwość przewijania tekstu. Chcąc 
później wydrukować przeglądany raport, wystarczy zainicjować drukowanie 
przypisaniem 

AssignPrn

 i wpisać wartość 

Lines

  własności 

TMemo

 obiektu 

drukarki. 

Zachowujemy i kompilujemy projekt, a następnie uruchamiamy program. Po 
ukazaniu się  głównego okna aplikacji wciskamy klawisz F10, aby wydrukować 
formularz przy pomocy napisanego przed chwilą programu.