13 Rozdziae 12id 14782 Nieznany (2)

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”)

z

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

i

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

a

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.


Wyszukiwarka

Podobne podstrony:
13 Rozdzial 12 PHHJ6MRRUZXKRYO3 Nieznany
13 rozdzial 12 cyxgxyclvhrmz4yc Nieznany (2)
13 rozdzial 13 NSBB2L5SXN4GTHOI Nieznany (2)
13 12id 14347 Nieznany
13 rozdzial 13 CPGH6VGICGVO5PQ4 Nieznany (2)
13 T 12id 14342 Nieznany (2)
13 Produkowanie kielbasid 14752 Nieznany
05 rozdzial 04 nzig3du5fdy5tkt5 Nieznany (2)
28 rozdzial 27 vmxgkzibmm3xcof4 Nieznany (2)
17 rzs 2012 13 net wersja pods Nieznany (2)
Cwiczenia nr 13 RPiS id 124686 Nieznany
13 Sporzadzanie mapy sytuacyjno Nieznany
13 Zdobienie i wykonczanie wyro Nieznany (2)
22 Rozdzial 21 KP4Q5YBIEV5DBSVC Nieznany (2)
09 08 Rozdzielnice budowlane RB Nieznany (2)
17 rozdzial 16 fq3zy7m2bu2oan6t Nieznany (2)
Kanicki Systemy Rozdzial 10 id Nieznany
29 rozdzial 28 ciw47mwstcqakqpq Nieznany

więcej podobnych podstron