18 (18) doc


Rozdział 18.
Tworzenie aplikacji bazodanowych


Trudno jest zawrzeć w krótkim rozdziale całość problematyki tworzenia aplikacji bazodanowych, więc i my z konieczności ograniczymy się tylko do najważniejszych kwestii.

Ponieważ wiesz już w jaki sposób tworzy się formularze baz danych, większą cześć tego rozdziału poświęcimy aspektom programowania baz danych na niższym poziomie. Na początku zapoznasz się z tworzeniem i wypełnianiem baz danych w sposób całkowicie programowy, następnie przejdziesz do zagadnienia modułów danych, by pod koniec rozdziału przyjrzeć się tworzeniu raportów baz danych przy użyciu komponentów z grupy QuickReport. Całość zostanie zamknięta omówieniem tematu dystrybucji aplikacji bazodanowych.

Niewizualny aspekt
programowania bazodanowego

Dotychczasowe ćwiczenia z poprzednich rozdziałów traktujących o programowaniu baz danych miały w przeważającej większości charakter prezentacyjny - ich zasadniczym przeznaczeniem było wyświetlanie i edycja danych. W niniejszej sekcji przyjrzymy się mniej spektakularnym - bo nie widocznym bezpośrednio - lecz równie ważnym mechanizmom.

0x01 graphic

Przykładowe ćwiczenia prezentowane w niniejszej sekcji ograniczają się z konieczności do komponentu TTable; pouczającym ćwiczeniem może być stworzenie analogicznych przykładów dla komponentu TQuery, z wykorzystaniem języka SQL.

Czytanie z bazy danych

Czytanie zawartości bazy danych nie jest w żadnym razie niczym nadzwyczajnym, toteż i pierwszy prezentowany przykład będzie również bardzo prosty - zawartość wybranych kolumn tabeli CUSTOMER.DB zostanie zapisana do pliku tekstowego, a poszczególne kolumny zostaną oddzielone przecinkami.

Trzeba w tym celu stworzyć obiekt klasy TTable, następnie skojarzyć go z konkretnym aliasem i konkretną tabelą w ramach aliasu. Kod wykonujący te operacje wygląda następująco:

var

Table : TTable;

begin

Table := TTable.Create(Self);

Table.DatabaseName := 'DBDEMOS';

Table.TableName := 'Customer.db';

end;

Powyższy fragment jest odpowiednikiem tego, co czyni Delphi podczas ustawiania właściwości DatabaseName i TableName w Inspektorze Obiektów.

Kolejnym krokiem będzie odczytanie każdego z rekordów bazy danych i zapisanie go do pliku w postaci linii tekstu. Najpierw zaprezentowana zostanie podstawowa struktura (bez rzeczywistego kodu) służąca do zapisywania pól bazy danych do pliku tekstowego, później przejdziemy do szczegółów. Ta podstawowa struktura wygląda następująco:

Table.Active := True;

while not Table.Eof do

begin

{Znajdujący się tutaj kod czyta wartości pól z bieżącego }

{rekordu i zapisuje je do pliku tekstowego }

Table.Next;

end;

Table.Free;

Działanie powyższego kodu jest niemal oczywiste. Przypisanie na początku wartości True właściwości Active powoduje otwarcie tabeli (identyczny efekt dałoby wywołanie metody Open). Następnie poszczególne rekordy tabeli są w ramach pętli odczytywane i zapisywane jako kolejne linie pliku tekstowego. Przejście do następnego rekordu odbywa się w wyniku wywołania metody Next, zaś kryterium zakończenia pętli jest badanie wyczerpania się rekordów (tj. próba przejścia poza ostatni rekord - metoda Eof zwraca wówczas wartość True). Po wykonaniu zadania obiekt Table jest zwalniany.

Naturalnie, żeby móc zapisać wartość każdego z pól do pliku tekstowego, wcześniej informację tę trzeba wydobyć z samego pola. W tym celu trzeba zastosować metodę FieldByName i właściwość AsString klasy TField. Była już o tym mowa w rozdziale 16. w sekcji „Dostęp do pól”. Pierwszym interesującym nas polem tabeli CUSTOMER.DB jest pole CustNo. Wydobycie wartości z tego pola może odbyć się w sposób następujący:

0x01 graphic

Obiekt klasy TTable nie musi być zwalniany w sposób jawny - zostanie bowiem automatycznie zwolniony w procesie zwalniania formularza; formularz jest jego komponentem-właścicielem, co znajduje swe odzwierciedlenie w wywołaniu konstruktora:

Table := TTable.Create(Self);

(zmienna Self oznacza tutaj oczywiście formularz). Dobrą praktyką w programowaniu jest jednakże jawne zwalnianie niepotrzebnych już obiektów - co też uczynilismy.

var

S : string;

begin

S := Table.FieldByName('CustNo').AsString + ',';

Zauważ, że do otrzymanego w ten sposób łańcucha dodawany jest na końcu znak przecinka, dzięki czemu wartość pola zostanie odseparowana od następnych pól. Powyższy kod powtórzony zostanie dla pozostałych pól, których wartości nas interesują. Cała sekwencja poleceń została przedstawiona w dwóch modułach (listingi 18.1 i 18.2) składających się na program o nazwie MakeText, który został również umieszczony na dołączonej do książki dyskietce. Ten krótki program przetwarza tabelę CUSTOMER.DB i na jej podstawie tworzy plik tekstowy o nazwie CUSTOMER.TXT. Listing 18.1 przedstawia moduł formularza głównego (MakeTxtU.pas); w formularzu tym znajduje się jedynie przycisk i komponent Memo. Przyjrzyj się wpierw obydwu listingom, później przystąpimy do omówienia zasady ich działania.

Listing 18.1. MakeTxtU.pas

unit MakeTxtU;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,

Forms, Dialogs, StdCtrls, DbTables;

type

TForm1 = class(TForm)

CreateBtn: TButton;

Memo: TMemo;

procedure CreateBtnClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.CreateBtnClick(Sender: TObject);

var

Table : TTable;

S : string;

begin

{ Utworzenie obiektu Table oraz przypisanie mu nazwy bazy }

{ danych i nazwy tabeli. }

Table := TTable.Create(Self);

Table.DatabaseName := 'DBDEMOS';

Table.TableName := 'Customer.db';

{ Zmiana kursora na klepsydrę. }

Screen.Cursor := crHourGlass;

{ Można użyć komponentu Memo do pokazania postępu zadania}

{ i jednocześnie zapisania pliku na dysk. Najpierw obiekt }

{ Memo musi zostać wyczyszczony z wszelkiego tekstu. }

Memo.Lines.Clear;

{ Otwarcie tabeli. }

Table.Active := True;

CreateBtn.Enabled := False;

{ Pętla zapisująca kolejne rekordy do komponentu Memo. }

while not Table.Eof do begin

{ Pobranie pierwszego pola i dodanie go do łańcucha S, }

{ umieszczenie na końcu łańcucha znaku przecinka. }

S := Table.FieldByName('CustNo').AsString + ',';

{ Powtórzenie identycznego kroku dla wszystkich (wymaganych

{ przez nas) pól. }

S := S + Table.FieldByName('Company').AsString + ',';

S := S + Table.FieldByName('Addr1').AsString + ',';

S := S + Table.FieldByName('Addr2').AsString + ',';

S := S + Table.FieldByName('City').AsString + ',';

S := S + Table.FieldByName('State').AsString + ',';

S := S + Table.FieldByName('Zip').AsString + ',';

S := S + Table.FieldByName('Phone').AsString + ',';

S := S + Table.FieldByName('FAX').AsString + ',';

{ Dodanie łańcucha do komponentu Memo. }

Memo.Lines.Add(S);

{ Przejście do kolejnego rekordu. }

Table.Next;

end;

{ Zapisanie zawartości komponentu Memo do pliku tekstowego }

Memo.Lines.SaveToFile('customer.txt');

{ Przywrócenie poprzedniego kursora i jego ponowne udostępnienie }

CreateBtn.Enabled := True;

Screen.Cursor := crDefault;

Table.Free;

end;

end.

Cała akcja rozgrywa się we wnętrzu metody CreateBtnClick. W wyniku kliknięcia na przycisku Create File (utwórz plik),program wydobywa dane z tabeli bazy danych i umieszcza je w komponencie Memo. Jako pierwsza w łańcuchu umieszczana jest wartość pola CustNo, za nią dodawany jest przecinek. Potem do końca łańcucha dołączane są konsekwentnie kolejne wartości pól, po których ponownie występuje przecinek. Kiedy wszystkie dane zostaną odczytane z rekordu, łańcuch jest dodawany do komponentu Memo (jako jego kolejna linia) przy użyciu metody Add. Po osiągnięciu końca tabeli zawartość komponentu Memo jest zapisywana do pliku tekstowego.

Komponent Memo został tutaj użyty z dwóch powodów. Po pierwsze, wyświetlanie wyników w jego oknie (p. rys. 18.1) umożliwia obserwowanie tego, co generuje program. Po drugie, właściwość Lines (klasy TStrings) oferuje łatwy sposób zapisania poszczególnych linii do pliku tekstowego.

Rysunek 18.1.

Program MakeText w czasie pracy

0x01 graphic

Tworzenie tabel baz danych w sposób programowy

Wygodnym narzędziem do tworzenia baz danych jest np. Database Desktop. Niekiedy jednak aplikacja musi tworzyć tabele w sposób programowy (na przykład w sytuacji, gdy zestaw i właściwości pól tabeli nie są znane a priori przed wykonaniem programu). Utworzenie tabeli w kodzie programu wymaga następujących czynności:

  1. Utworzenia aliasu BDE dla bazy danych.

  2. Utworzenia obiektu klasy TTable.

  3. Dodania definicji pól do właściwości FieldDefs.

  4. Dodania definicji indeksów do właściwości IndexDefs (jeżeli zakładamy, że tabela będzie posiadać indeksy).

  5. Utworzenia rzeczywistej tabeli przy użyciu metody CreateTable.

Tworzenie aliasu BDE i obiektu klasy TTable

Pierwsze dwa kroki miałeś okazję wykonać już wcześniej w rozdziale 16. kiedy mowa była o tworzeniu aliasów BDE oraz w poprzedniej sekcji, gdzie tworzyłeś obiekt klasy TTable. Poniżej znajduje się krótkie przypomnienie obu tych kroków:

var

Table : TTable;

begin

{ Utworzenie aliasu. }

CreateDirectory('C:\LPARADOX', nil);

Session.AddStandardAlias('MyDatabase', 'C:\LPARADOX',

'PARADOX');

{ zapisanie zmian w pliku konfiguracyjnym BDE }

Session.SaveConfigFile;

{ Utworzenie tabeli. }

Table := TTable.Create(Self);

Table.DatabaseName := 'MyDatabase';

Table.TableName := 'MyTable.db';

end;

W powyższym fragmencie kodu tworzony jest katalog LPARADOX (nazwa ta jest jedynie przykładowa), który następnie obsadzony zostaje w roli aliasu bazy danych typu Paradox.

Następnie tworzony jest obiekt klasy TTable, przy czym jego właściwości DatabaseName przypisywany jest utworzony przed chwilą alias, zaś właściwości TableName - nazwa tabeli, którą mamy zamiar utworzyć.

Na razie utworzyliśmy obiekt klasy TTable - sama tabela (jako plik dyskowy) jeszcze nie istnieje.

Definiowanie pól

Następny krok polega na zdefiniowaniu pól dla nowo tworzonej tabeli. Definicja każdego pola określa jego typ, rozmiar (nie we wszystkich przypadkach) a także informację o tym, czy pole jest w tabeli wymagane. Typ pola może być jedną z wartości typu TFieldType, przedstawionych w tabeli 18.1.

Tabela 18.1. Typy pól tabeli bazy danych

Typ pola

Opis

ftUnknown

Nieznany lub nieokreślony

ftString

Pole znakowe lub łańcuchowe

ftSmallint

16-bitowa wartość całkowita ze znakiem (SmallInt)

ftInteger

32-bitowa wartość całkowita ze znakiem (Longint)

ftWord

16-bitowa wartość całkowita bez znaku (Word)

ftBoolean

Wartość boolowska

ftFloat

Wartość zmiennoprzecinkowa

ftCurrency

Kwota pieniężna

ftBCD

Wartości dziesiętna kodowanej binarnie (BCD - Binary-Coded Decimal)

ftDate

Data

ftTime

Czas

ftDateTime

Data i czas

Tabela 18.1. cd. Typy pól tabeli bazy danych

Typ pola

Opis

ftBytes

Pole binarne stałej długości

ftVarBytes

Pole binarne zmiennej długości

ftAutoInc

Pole samoinkrementujące - 32-bitowa liczba całkowita automatyczni zwiększana przy każdym odwołaniu do pola

ftBlob

Pole dużego obiektu binarnego (BLOB- binary large object)

ftMemo

Pole Memo

ftGraphic

Bitmapa

ftFmtMemo

Sformatowane pole Memo

ftParadoxOle

Pole obiektu OLE Paradox'a

ftDBaseOle

Pole obiektu OLE dBASE'a

ftTypedBinary

Typowane pole binarne

Definicję pola tworzy się za pośrednictwem właściwości FieldDefs klasy TTable. Właściwość ta jest obiektem klasy TFieldDefs, której metoda Add służy do wprowadzenia kolejnej definicji pola. Oto przykład:

Table.FieldDefs.Add('Customer', ftString, 30, False);

Powyższa linia kodu dodaje definicję pola łańcuchowego o nazwie Customer i rozmiarze 30 znaków. Pole to nie jest obowiązkowe, ponieważ ostatnim parametrem wywołania jest False.

Tworzenie definicji indeksów

Jeżeli tabela będzie indeksowana, trzeba również zadbać od dodanie jednej lub kilku definicji indeksów. Dodawanie definicji indeksów jest operacją podobną do definiowania pól. Zestaw indeksów tabeli określony jest przez jej właściwość IndexDefs klasy TindexDefs; aby zdefiniować kolejny indeks należy wywołać jej metodę Add z odpowiednimi parametrami:

Table.IndexDefs.Add('','CustNo', [ixPrimary]);

Pierwszy parametr wywołania określa nazwy indeksu. Jeżeli tworzony jest indeks główny (jak w wyżej), nie trzeba określać jego nazwy. Drugi parametr służy do określenia pola (lub ciągu pól, oddzielonych przecinkami) które będą tworzyły klucz indeksu - na przykład:

Table.IndexDefs.Add('', Room;Time', [ixPrimary]);

Ostatni parametr (typu TindexOptions) określa charakter indeksu - przez kombinację jego elementów można określać takie atrybuty indeksu, jak hierarchia (główny/drugorzędny), kierunek uporządkowania rekordów (rosnący/malejący), wrażliwosć na małe/duże litery itp. Szczegółowy opis każdej opcji znajduje się w systemie pomocy Delphi; poniższy przykład definiuje indeks główny porządkujący rekordy tabeli w kolejności malejącej:

Table.IndexDefs.Add('', 'CustNo', [ixPrimary, ixDescending]);

Tworzenie tabeli

Po zdefiniowaniu pól i indeksów tabeli pozostaje tylko jej fizyczne utworzenie - jest to operacja najprostsza z możliwych:

Table.CreateTable;

Metoda CreateTable dokonuje rzeczywistego utworzenia plików dyskowych składających się na (pustą) tabelę - korzysta ona oczywiście z właściwości FieldDefs oraz IndexDefs.

Po utworzeniu tabeli można przystąpić do wypełniania jej danymi - w dowolny sposób: programowo lub poprzez odpowiedni formularz.

Dołączona do książki dyskietka zawiera program o nazwie MakeTabl. Program ten tworzy tabelę, a następnie wypełnia ją danymi pochodzącymi z pliku CUSTOMER.TXT (utworzonego przez omawiany wcześniej program MakeText). Listing 18.2 przedstawia moduł głównego formularza tej aplikacji (MakeTblU.pas); zwróć uwagę na obecność modułów DBGrids, DBTables i DB w jego liście uses.

Listing 18.2. MakeTblU.pas

unit MakeTblU;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,

Forms, Dialogs, StdCtrls, Grids, DBGrids, DbTables, Db;

type

TForm2 = class(TForm)

DBGrid: TDBGrid;

CreateBtn: TButton;

FillBtn: TButton;

procedure CreateBtnClick(Sender: TObject);

procedure FillBtnClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.CreateBtnClick(Sender: TObject);

const

Dir : PChar = 'c:\mydata';

var

Table : TTable;

begin

{ Utworzenie aliasu BDE, o ile nie istniał on }

{ do tej pory. }

if not Session.IsAlias('MyDatabase') then begin

CreateDirectory(Dir, nil);

try

Session.AddStandardAlias('MyDatabase', Dir, 'PARADOX');

Session.SaveConfigFile;

except

MessageDlg('Error Creating Alias', mtError, [mbOk], 0);

Exit;

end;

end;

Screen.Cursor := crHourGlass;

{ Utworzenie tabeli, }

Table := TTable.Create(Self);

try

Table.DatabaseName := 'MyDatabase';

Table.TableName := 'MyTable.db';

{ Dodanie definicji pól. }

Table.FieldDefs.Add('CustNo', ftFloat, 0, True);

Table.FieldDefs.Add('Customer', ftString, 30, False);

Table.FieldDefs.Add('Addr1', ftString, 30, False);

Table.FieldDefs.Add('Addr2', ftString, 30, False);

Table.FieldDefs.Add('City', ftString, 15, False);

Table.FieldDefs.Add('State', ftString, 20, False);

Table.FieldDefs.Add('Zip', ftString, 10, False);

Table.FieldDefs.Add('Phone', ftString, 15, False);

Table.FieldDefs.Add('Fax', ftString, 15, False);

{ Dodanie definicji indeksu dla klucza głównego }

Table.IndexDefs.Add('', 'CustNo', [ixPrimary]);

{ Po dokonaniu wszelkich ustawień można utworzyć tabelę. }

Table.CreateTable;

except

MessageDlg('Error Creating Table', mtError, [mbOk], 0);

Screen.Cursor := crDefault;

Table.Free;

Exit;

end;

{ Poinformowanie użytkownika o zakończeniu procesu tworzenia.}

Table.Free;

Screen.Cursor := crDefault;

CreateBtn.Enabled := False;

FillBtn.Enabled := True;

MessageDlg('Table Created Successfully', mtInformation, [mbOk], 0);

end;

procedure TForm2.FillBtnClick(Sender: TObject);

var

Table : TTable;

Datasource : TDataSource;

Lines : TStringList;

S, S2 : string;

I, P : Integer;

begin

{ Create a TTable. }

Table := TTable.Create(Self);

Table.DatabaseName := 'MyDatabase';

Table.TableName := 'MyTable.db';

{ Utworzenie obiektu źródła danych i połączenie go }

{ z tabelą. Połączenie obiektu siatki ze źródłem danych. }

Datasource := TDataSource.Create(Self);

Datasource.DataSet := Table;

DBGrid.Datasource := Datasource;

{ Otwarcie tabeli i pliku tekstowego. }

Table.Active := True;

Lines := TStringList.Create;

Lines.LoadFromFile('customer.txt');

{ Ustawienie tabeli w tryb edycji. }

Table.Edit;

{ Przetwarzanie kolejnych linii właściwości Lines. }

for I := 0 to Pred(Lines.Count) do begin

{ Dodanie rekordu do końca zbioru. }

Table.Append;

{ Rozczłonkowanie łańcucha w celu uzyskania pierwszego członu }

S := Lines[I];

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

{ Zapisanie wartości w polu CustNo. }

Table.FieldByName('CustNo').Value := StrToInt(S2);

{ Powtórzenie tego samego procesu dla pozostałych pól. }

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('Customer').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('Addr1').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('Addr2').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('City').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('State').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('Zip').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('Phone').Value := S2;

P := Pos(',', S);

S2 := Copy(S, 1, P - 1);

Delete(S, 1, P);

Table.FieldByName('FAX').Value := S2;

{ Próbując dodać rekord o takim samym numerze klienta otrzymamy }

{ wyjątek naruszenia unikalności klucza. Jeżeli tak się zdarzy, }

{ musimy poinformować o tym użytkownika, anulować modyfikację, }

{ przywrócić tabelę do trybu edycji i kontynuować przetwarzanie }

{ kolejnych linii. }

try

Table.Post;

except

on EDBEngineError do begin

MessageBox(Handle,

'Duplicate Customer Number', 'Key Violation', 0);

Table.Cancel;

Table.Edit;

Continue;

end;

end;

end;

{ Obiekt klasy TStringList nie jest już dłużej potrzebny, }

{ można więc go zwolnić. }

Lines.Free;

{ Nie będziemy usuwać z pamięci tabeli, dzięki czemu jej dane }

{ będą dalej wyświetlane przez obiekt DBGrid. Zwolnienia tabeli }

{ i źródła danych dokona za nas VCL. }

{Table.Free; }

{Datasource.Free; }

end;

end.

Moduły danych i ich użytkowanie

Pożądaną cechą każdego projektu jest coś, co można by nazwać rozdzielaniem problemów - na podłożu tej idei zrodziło się właśnie programowanie obiektowe (OOP). Jak ma się to do aplikacji bazodanowych? Po dotychczasowej lekturze zdajesz sobie zapewne sprawę ze sposobu, w jaki komponenty bazodanowe reprezentują w aplikacji bazę danych jako całość - czyli zbiory danych (TTable/TQuery), komponenty źródłowe (TDataSource), komponenty pól (TField), komponenty TDataBase i TSession z wzajemnymi powiązaniami. Wygodnie byłoby wyodrębnić w tę część aplikacji - na przykład po to, by uniknąć wielokrotnego jej budowania w ten sam sposób w różnych formularzach.

Wyodrębnienie takie możliwe jest dzięki specjalizowanym formularzom, które nazywane są modułami danych (ang. Datamodules). Tworzenie modułu danych najlepiej rozpocząć od wybrania z Repozytorium (okno New Items) pozycji oznaczonej Data Module. W wyniku tego Delphi utworzy pusty moduł danych i odpowiadający mu moduł źródłowy, które możesz zapisać na dysku pod wskazaną nazwą - identycznie, jak w przypadku „zwykłego” formularza. Teraz można zacząć zapełnianie modułu danych komponentami dostępu do danych; kiedy czynność ta zostanie ukończona, można zapisać moduł na dysk.

Każdy moduł danych aplikacji widoczny jest dla każdego jej formularza.

0x01 graphic

Ze względu na charakter modułu danych nie można umieszczać w nim komponentów wizualnych (nie pozwoli na to Projektant Formularzy). Oprócz - oczywistych w tym kontekście - komponentów dostępu do danych moduł danych może natomiast zawierać komponenty zaliczane do niewidocznych, jak TTimer, TMainMenu itp.

Przykładowy moduł danych

Proste ćwiczenie pomoże Ci lepiej zrozumieć istotę modułów danych. Zaczniemy od stworzenia modułu danych, a następnie spróbujemy znaleźć dla niego zastosowanie:

  1. Zainicjuj nową aplikację. Zmień właściwość Name głównego formularza na MainForm. Zapisz projekt - formularzowi głównemu nadaj nazwę DSExMain.pas, natomiast plik projektu zapisz pod nazwą DSExampl.dpr.

  1. Wybierz polecenie File | New. Kiedy pojawi się okno Repozytorium, kliknij dwukrotnie na ikonie Data Module, w wyniku czego Projektant Formularzy stworzy nowy moduł danych; zmień jego nazwę (właściwość Name) na DBDemos.

  2. Ze strony Data Access Palety Komponentów wybierz komponent Table i umieść go w module danych. Zmień właściwość DatabaseName (komponentu Table) na DBDEMOS, a właściwość TableName na ANIMALS.DBF. Nazwij tabelę AnimalTable (właściwość Name).

  3. Umieść w module danych drugi komponent Table. Ustaw jego właściwość DatabaseName na DBDEMOS, a właściwość TableName na BIOLIFE.DB. Właściwości Name przypisz wartość BiolifeTable.

  4. Umieść w module komponent DataSource. Zmień jego właściwość Name na Animals oraz właściwość DataSet na AnimalsTable.

  5. Dodaj do modułu danych kolejny komponent DataSource. Zmień jego nazwę (Name) na Biolife i połącz go (właściwość DataSet) z komponentem BiolifeTable. Moduł danych powinien wyglądać teraz tak, jak na rysunku 18.2.

  6. Rysunek 18.2.

    Ukończony moduł danych

    0x01 graphic

    1. Kliknij podwójnie na tle modułu danych. Stworzona zostanie procedura obsługująca zdarzenie OnCreate - wpisz do jej wnętrza dwie linie kodu:

    AnimalsTable.Open;

    BiolifeTable.Open;

    1. Zapisz moduł pod nazwą DataMod.pas.

    Wykorzystanie modułu danych w praktyce

    Spróbujmy teraz wykorzystać stworzony przed chwilą moduł. Do głównego formularza aplikacji dodaj dwa przyciski; jeden z nich będzie służył do wyświetlania formularza, który pokazuje tabelę Animals, a drugi do wyświetlania formularza pokazującego tabelę Biolife.

    1. Utwórz nowy formularz. Zmień jego właściwość Caption na Animals Form i właściwość Name na AnimalsForm.

    1. Wybierz polecenie File | Use Unit, w otwartym oknie dialogowym wybierz moduł DataMod i kliknij na przycisku OK; od tej chwili moduł danych jest dostępny z poziomu formularza głównego.

    2. Umieść w formularzu komponenty DBGrid i DBNavigator. Zaznacz obydwa, w Inspektorze Obiektów zlokalizuj właściwość DataSource i kliknij na przycisku rozwinięcia listy. Wśród dostępnych źródeł danych zobaczysz dwa następujące elementy:

    DBDemos.Animals

    DBDemos.Biolife

    Wybierz pozycję DBDemos.Animals.

    1. Zapisz moduł pod nazwą DSExU2.pas (lub inną bardziej znaczącą).

    2. Ponownie dodaj nowy formularz do projektu. Powtórz kroki od 1 do 3, ale tym razem jako źródło danych wybierz DBDEMOS.Biolife i zmień właściwość Caption na Biolife Form. Zmień również właściwość Name na BiolifeForm. Zapisz formularz pod nazwą DSExU3.pas. Rysunek 18.3 przedstawia środowisko IDE po wykonaniu tego kroku.

    Ostateczne wykończenie aplikacji

    Powyższe kroki pokazują, że po stworzeniu modułu danych zawarte w nim komponenty mogą być użyte w dowolnym miejscu programu. Wystarczy zastosować odpowiedni moduł, a wszystkie komponenty wrażliwe na dane będą w stanie wykryć moduł danych. Dokończmy teraz aplikację, aby można było ją wypróbować:

    Rysunek 18.3.

    Ukończony drugi formularz aplikacji

    0x01 graphic

    1. Umieść w głównym formularzu przycisk i zmień jego właściwość Caption na Show Animals.

    1. Kliknij podwójnie na przycisku, aby stworzyć procedurę obsługującą jego zdarzenie OnClick; we wnętrzu tej procedury umieść następującą linię kodu:

    AnimalsForm.Show;

    1. Umieść w formularzu kolejny przycisk i zmień właściwość Caption na Show Biolife.

    2. Stwórz procedurę obsługująca zdarzenie OnClick dla tego przycisku i dodaj linię kodu do jej wnętrza:

    BiolifeForm.Show;

    1. Wybierz polecenie File | Use Unit. Wybierz moduł DSExU2 i kliknij na przycisku OK.

    2. Powtórz krok 5 dodając tym razem moduł DSExU3.

    Uruchom program. Kiedy klikniesz na jednym z przycisków, wyświetlony zostanie odpowiedni formularz. Efekt działania programu przedstawia rysunek 18.4.

    Moduły danych pozwalają na łatwe konfigurowanie komponentów bazodanowych, a następnie wielokrotne ich używanie. Po stworzeniu modułu danych można zapisać go do Repozytorium, gdzie będzie on zawsze gotowy do wykorzystania.

    Rysunek 18.4.

    Program w trakcie pracy, z wyświetlonymi dwoma formularzami

    0x01 graphic

    Tworzenie raportów

    Żadna aplikacja bazodanowa nie byłaby kompletną bez możliwości przeglądania i drukowania danych. Do realizacji tych dwóch zadań służą właśnie raporty. Dotychczas miałeś okazję zaobserwować wyświetlanie pojedynczych rekordów (lub ich grup) za pomocą komponentu DBGrid; dla wielu aplikacji okazuje się to wystarczające, przyjdzie jednak taki moment, kiedy wyświetlanie czy drukowanie rekordów będzie musiało być bardziej wyszukane. Niezwykle pomocne w tym dziele okazują się komponenty grupy QuickReport.

    Przegląd komponentów grupy QuickReport

    Zanim będziesz mógł stworzyć jakikolwiek raport, musisz poznać działanie komponentów należących do grupy QuickReport.

    Komponent QuickRep

    Podstawowym komponentem grupy QuickReport jest QuickRep. Komponent ten służy jako płótno, na którym można inne komponenty będące składnikami raportu (zajmiemy się nimi za chwilę).

    Komponent QuickRep posiada właściwości, które wpływają na wygląd raportu po wydrukowaniu. Przykładowo, właściwość Page jest klasą zawierającą właściwości o nazwach TopMargin, BottomMargin, LeftMargin, RightMargin, Columns, Orientation, PaperSize i inne.

    Również właściwość PrinterSettings reprezentuje klasę i posiada własne właściwości: Copies, Duplex, FirstPage, LastPage i OutputBin. Właściwość ReportTitle służy do wyświetlenia opisu zadania drukowania, jaki pojawia się w oknie Menedżera Wydruków, a także na pasku tytułu okna podglądu QuickReport. Właściwość Units określa, czy marginesy wyświetlane są w milimetrach, calach, pikselach lub innych jednostkach. DataSet służy do wyspecyfikowania zbioru danych, z którego czerpane będą informacje dla raportu.

    0x01 graphic

    Zanim cokolwiek pojawi się w raporcie, właściwość DataSet musi być ustawiona, a związany z nią zbiór danych uaktywniony.

    Do podstawowych metod komponentu QuickRep należą Preview i Print. Metoda Print, jak wskazuje jej nazwa, drukuje raport. Metoda Preview wyświetla modalne okno podglądu, w skład którego wchodzą przyciski opcji podglądu, przejścia do pierwszej, ostatniej, poprzedniej i następnej strony, drukowania, ustawień drukowania, zachowania raportu, otwarcia raportu i zamknięcia podglądu. Okno podglądu QuickReport w trakcie pracy przedstawione zostało na rysunku 18.5.

    Rysunek 18.5.

    Okno podglądu QuickReport

    0x01 graphic

    Do godnych uwagi zdarzeń komponentu QuickRep zaliczają się OnPreview i OnNeedData. Zdarzenie OnPreview można wykorzystać do stworzenia niestandardowego okna podglądu wydruku. Zdarzenie OnNeedData jest wykorzystywane, gdy korzystamy ze źródła danych innego niż baza danych VCL. Dla przykładu, raport można stworzyć z listy łańcuchów, tablicy lub pliku tekstowego.

    Wstęgi raportu

    Raport składa się z różnego typu wstęg (ang. bands). Prosty raport posiada przynajmniej trzy typy wstęg: tytułową (title band), nagłówkową kolumn (column header band) i szczegółową (detail band).

    Wstęga tytułowa zawiera nazwę raportu, która wyświetlana jest tylko na jego pierwszej stronie.

    Wstęga nagłówków kolumn służy do wyświetlenia nazw kolumn związanych z polami w zbiorze danych; pojawia się ona na szczycie każdej strony. Nie posiadają jej niektóre raporty, na przykład te służące do generowania etykiet adresowych.

    Największy nakład pracy wiąże się ze wstęgą szczegółową; to właśnie w niej umieszczane są wszelkie dane, które znaleźć się mają w raporcie. QuickReport powtarza tę wstęgę dla każdego rekordu w zbiorze danych. Za chwilę wykonasz ćwiczenie, które ilustruje działanie opisywanych wstęg.

    Do innych często stosowanych typów wstęg zaliczyć można nagłówek strony, stopkę strony, nagłówek grupy, stopkę grupy i podsumowanie. Wstęgi reprezentowane są przez komponenty QRBand; typ wstęgi określa właściwość BandType.

    Układ wstęg umieszczanych na płótnie komponentu QuickRep jest automatycznie aranżowany w zależności od konkretnej sytuacji; jeżeli na przykład zmienisz typ wstęgi (właściwość BandType) na stopkę strony (rbPageFooter) wstęga ta zostanie przeniesiona poniżej wszystkich pozostałych wstęg. Analogicznie, wstęga nagłówka strony zostanie umieszczona nad wszystkimi istniejącymi wstęgami.

    Elementy składające się na projekt raportu

    Elementy projektu raportu dzielą się zasadniczo na trzy części. W skład pierwszej wchodzą komponenty przeznaczone dla etykiet tekstowych, obrazów, kształtów, nagłówków, stopek, itp. Ich podstawowym celem jest wyświetlanie statycznych elementów projektu. Przykładowo, tytuł raportu jest ustawiany zazwyczaj tylko raz i później już nie jest zmieniany. Innym przykładem może być element graficzny prezentujący na formularzu logo firmy. Komponenty tej grupy są bardzo podobne do standardowych komponentów VCL - QRLabel przypomina standardowy komponent Label, QRImage podobny jest do komponentu Image, QRShape to odpowiednik standardowego komponentu Shape,itd. Stosuje się je przy projektowaniu statycznych elementów raportu.

    Druga kategoria elementów to odpowiedniki komponentów VCL wrażliwych na dane (ang. data-aware components). Komponenty te (QRDBText, QRDBRichEdit, QRDBImate) umieszczane są we wstęgach szczegółowych i reprezentują dane pobierane ze zbioru danych.

    Trzecią grupę komponentów QuickReport tworzą QRSysData i QRExpr. Komponent QRSysData służy do wyświetlania numerów stron, daty i czasu raportu, jego tytułu i kilku jeszcze innych rzeczy. Celem komponentu QRExpr jest wyświetlanie wyrażeń obliczanych. Do zdefiniowania takiego wyrażenia służy właściwość Expression. Definiowanie prostych wyrażeń umożliwia specjalny edytor właściwości Expression - Expression builder. Złożoność wyrażenia bywa zróżnicowana - od prostego mnożenia dwóch pól do wywoływania formuł AVERAGE, COUNT, SUM itp.

    Ręczne tworzenie raportów

    Z całą pewnością najbardziej elastycznym sposobem budowania raportów jest tworzenie ich „na piechotę”. Jakkolwiek groźnie by to zabrzmiało, to na szczęście komponenty QuickReport zadanie to wielce ułatwiają. Najlepszą metodą wytłumaczenia sposobu ręcznego tworzenia formularzy będzie wykonanie ćwiczenia, w którym stworzymy aplikację wyświetlającą i drukującą raport w formie listy. Tym razem nie zostanie opisany każdy szczegółowy krok ćwiczenia - nie będzie na przykład mowy o zapisaniu projektu czy też nazwach plików, których należy użyć - kwestie te pozostawiam do samodzielnego rozwiązania. W trakcie pracy nie musisz się martwić o szczególną urodę raportu. Możesz powrócić później do tego przykładu i dopracować go.

    Pierwszy krok polega na utworzeniu głównego formularza aplikacji. Kiedy będzie to już gotowe, można przystąpić do tworzenia podstawowego zarysu raportu:

    1. Stwórz nową aplikację. Umieść w formularzu dwa przyciski. Zmień etykietę pierwszego z nich na Preview Report, a etykietę drugiego na Print Report.

    1. Wybierz polecenie File | New. W oknie Repozytorium kliknij podwójnie na ikonie Report; Delphi utworzy nowy formularz QuickReport.

    2. Do utworzonego formularza dodaj komponent Table. Zmień właściwość DatabaseName na DBDEMOS i właściwość TableName na EMPLOYEE.DB. Uaktywnij tabelę (Active = True).

    3. Wybierz formularz QuickReport. Zmień właściwość DataSet na Table1 i właściwość ReportTitle na Employee Report.

    4. Przejdź do formularza głównego i kliknij dwukrotnie na przycisku Preview Report. Do wnętrza procedury obsługującej zdarzenie OnClick wpisz następującą linię kodu:

    QuickReport2.Preview;

    1. Kliknij podwójnie na przycisku Print Report i wpisz następującą linię kodu do wnętrza procedury obsługującej zdarzenie OnClick:

    QuickReport2.Print;

    1. Wybierz polecenie File | Use Unit i dołącz moduł formularza QuickReport.

    Masz teraz przed sobą pusty raport; musisz dodać do niego wstęgę tytułową, wstęgę nagłówków kolumn i wstęgę szczegółową. W trakcie kolejnych kroków możesz konfrontować swoje poczynania z końcowym rezultatem przedstawionym na rysunku 18.6:

    1. Ze strony QReport Palety Komponentów wybierz komponent QRBand i umieść go w raporcie;. domyślnie reprezentuje on wstęgę tytułową.

    1. Wybierz komponent QRLabel i umieść go we wstędze tytułowej. Zmień jego właściwość Caption na Employee Report. Wybierz czcionkę dla tytułu (właściwość Font) według własnego uznania (w przykładzie użyta została czcionka Arial, 18 punktów, pogrubiona). Wycentruj komponent względem wstęgi.

    2. Umieść kolejną wstęgę w formularzu i zmień jej właściwość BandType na rbColumnHeader (wiersz nagłówków kolumn); nadaj czcionce atrybuty pogrubienia i podkreślenia.

    3. W lewej części wstęgi nagłówków umieść komponent QRLabel i zmień jego właściwość Caption na Employee Number. Umieść koleją etykietę QRLabel na prawo od poprzedniej i zmień jej właściwość Caption na Name. Umieść trzecią etykietę na prawo od poprzedniej, tym razem nadając jej wartość Salary.

    4. Umieść w raporcie kolejną wstęgę i zmień jej typ (BandType) na rbDetail (szczegóły). Zauważ, że po tej zmianie wstęga została przemieszczony poniżej pozostałych wstęg

    5. W lewej części wstęgi szczegółowej umieść komponent QRDBText (umieść go w jednej linii z etykietą Employee Number). Zmień jej właściwość DataSet na Table1 i właściwość DataField na EmpNo.

    6. Umieść kolejny komponent QRDBText we wstędze szczegółowej i wyrównaj go w pionie ze znajdującą się wyżej etykietą Name. Zmień jego właściwość DataSet na Table1 i właściwość DataField na FirstName. Dodaj następnie do wstęgi szczegółowej jeszcze jeden komponent do QRDBText, na prawo od poprzedniego (patrz rysunek 18.6.) i skojarz go z polem LastName tabeli.

    7. Dodaj ostatni komponent QRDBText do wstęgi szczegółowej. Umieść go pod etykietą Salary i połącz z polem Salary tabeli. Formularz powinien teraz wyglądać tak, jak na rysunku 18.6.

    Rysunek 18.6.

    Formularz QuickReport

    0x01 graphic

    Prawdopodobnie jesteś ciekaw, jak ów raport będzie wyglądał na papierze - możesz zobaczyć to już teraz. Kliknij prawym przyciskiem myszy na formularzu i wybierz polecenie menu kontekstowego Preview. Otwarte zostanie okno podglądu raportu QuickReport z raportem wyświetlonym w jego wnętrzu.

    Aby wydrukować raport, kliknij na przycisku Print. Kiedy skończysz przyglądać się raportowi, zamknij okno przyciskiem Close. Możesz teraz uruchomić program i wypróbować działanie przycisków Preview Report oraz Print Report.

    0x01 graphic

    Między liniami stworzonego przed chwilą raportu występują podwójne ostępy. Zmianę odległości między wierszami można uzyskać przez modyfikację wysokości wstęgi szczegółowej.

    Zanim zakończymy dyskusję na temat raportów, powinieneś poznać jeszcze jedną miłą cechę narzędzia QuickReport. Kliknij prawym przyciskiem na formularzu i wybierz polecenie menu kontekstowego Report settings. Wyświetlone zostanie okno dialogowe ustawień (rys. 18.7). Można tutaj w sposób wizualny ustawić podstawowe właściwości komponentu QuickRep, zamiast korzystać z Inspektora Obiektów.

    Rysunek 18.7.

    Okno dialogowe ustawień raportu

    0x01 graphic

    Tworzenie raportów w prosty sposób

    Repozytorium zawiera trzy gotowe formularze QuickReport (można znaleźć je na zakładce Forms): Quick Report Labels, Quick Report List i Quick Report Master/Detail. Modyfikując je stosownie do własnych potrzeb, można tworzyć potrzebne raporty w bardzo krótkim czasie.

    Dystrybucja bazodanowych aplikacji Delphi

    Jak wspomniałem w rozdziale szesnastym, BDE (Borland Database Engine) jest zbiorem bibliotek DLL i sterowników, które umożliwiają aplikacji komunikowanie się z różnymi typami baz danych. Jeżeli mamy zamiar dystrybuować aplikację korzystającą z BDE, musimy upewnić się, że odpowiednie pliki BDE zostały do niej dołączone i będą poprawnie rejestrowane w komputerach użytkowników.

    Najbardziej rozsądny sposób przeprowadzenia tego procesu opiera się na wykorzystaniu programu instalacyjnego zatwierdzonego przez firmę Borland. W firmie TurboPower korzystamy z programu Wise Install System będącego produktem firmy Great Lakes Business Solutions (http://www.glbs.com). Innym programem tego typu jest InstallShield i jego młodszy brat - InstallShield Express. Ten ostatni dostarczany jest wraz z Delphi (w wersji Professional i Client/Server).

    Być może zastanawiasz się, dlaczego firma Borland angażuje się w dyktowanie warunków dotyczących dystrybucji BDE. Powód tego jest bardziej oczywisty, niż mogłoby się wydawać - po prostu istnieje wiele wersji BDE, tak więc niektórzy użytkownicy korzystać będą z BDE wchodzącego w skład środowiska C++ Builder 1, inni być może stosują w swoich aplikacjach mechanizm BDE należący do środowiska Delphi 4 itp.

    BDE charakteryzuje się tzw. kompatybilnością wstecz - oznacza to, że nowsze wersje BDE będą poprawnie współpracować ze starszymi aplikacjami (lecz nie na odwrót!). Owa kompatybilność jest jednak zagwarantowana pod warunkiem przestrzegania pewnych reguł, dotyczących w pierwszym rzędzie zastępowania istniejących plików - w szczególności niedopuszczalne jest zastąpienie pliku jego starszą wersją. Licencjonowane programy instalacyjne sprawdzają numer wersji każdego z plików BDE, a więc korzystający z nich użytkownik będzie miał w swoim systemie pliki w najnowszej wersji.

    To jeszcze nie wszystko - licencjonowane programy instalacyjne potrafią określić, które pliki muszą być rozpowszechniane wraz z aplikacją. Powinieneś koniecznie przeczytać plik DEPLOY.TXT, znajdujący się w głównym katalogu Delphi, aby dowiedzieć się więcej na temat dystrybucji aplikacji korzystających z mechanizmu BDE.

    Podsumowanie

    Bez wątpienia tworzenie aplikacji bazodanowych wymaga wiele pracy. Pocieszające jest to, że Delphi czyni naszą pracę znacznie łatwiejszą niż inne środowiska programistyczne. W tym rozdziale poznałeś pewne zagadnienia związane z niewizualnym programowaniem baz danych. Dowiedziałeś się, czym są i do czego służą moduły danych. Pod koniec rozdziału zapoznałeś się z narzędziem QuickReport. Ułatwia ono tworzenie raportów wymaganych przez aplikację bazodanową. W końcu znalazło się również wyjaśnienie tego, co jest potrzebne podczas dystrybucji aplikacji współpracującej z bazą danych.

    Warsztat

    Warsztat składa się z pytań kontrolnych oraz ćwiczeń utrwalających i pogłębiających zdobytą wiedzę. Odpowiedzi do pytań możesz znaleźć w dodatku A.

    Pytania i odpowiedzi

    Prawdopodobnie nie wywołałeś metody CreateTable. Jej wywołanie jest niezbędne do fizycznego utworzenia tabeli.

    Tak. Wystarczy, że klikniesz prawym przyciskiem myszy na komponencie QuickRep i wybierzesz poleceniem menu kontekstowego Report settings. Otwarte zostanie okno dialogowe ustawień raportu (Report Settings), w którym w sposób wizualny można ustawić większość właściwości raportu.

    Tak. Moduł danych może zawierać wszelki kod niezbędny do przeprowadzenia operacji związanych z modułem danych. Mogą się tam znaleźć samodzielnie stworzone metody lub funkcje obsługujące zdarzenia. Utworzone metody mogą być publiczne lub prywatne (przeznaczone wyłącznie do użytku w ramach modułu).

    Najprawdopodobniej właściwość Active zbioru danych nie została ustawiona na wartość True. Aby raport mógł funkcjonować, zbiór danych musi wcześniej zostać otwarty.

    Nie. Chociaż teoretycznie możliwe jest umieszczenie w raporcie dwóch lub więcej wstęg szczegółowych, w chwili generowaniu raportu użyty zostanie tylko pierwsza z nich.

    W proces instalacji wmieszane są elementy biblioteki BDE, których poprawną instalację gwarantuje jedynie licencjonowany program instalacyjny.

    Quiz

    1. Jaką metodę należy wywołać, aby utworzyć tabelę bazy danych w czasie wykonania programu?

    1. Jakie przeznaczenie ma metoda Edit komponentu TTable?

    2. Jaką metodę należy wywołać, jeżeli chcemy zatwierdzić zmiany wprowadzone do rekordu?

    3. W jaki sposób tworzy się nowy moduł danych?

    4. Czy moduł danych jest standardowym formularzem?

    5. Jaką metodę należy wywołać, aby wydrukować raport typu QuickReport?

    6. Która ze wstęg służy do wyświetlania danych ze zbioru danych?

    7. Który z komponentów służy do wyświetlania numeru strony raportu?

    8. W jaki sposób można dokonać podglądu raportu w trakcie projektowania?

    9. Do czego służy komponent QRExpr?

    Ćwiczenia

    1. Stwórz bazę danych (alias BDE) i tabelę w tej bazie - programowo oraz korzystając z narzędzi bazodanowych Delphi.

    1. Stwórz moduł danych zawierający tabelę bazy danych z ćwiczenia pierwszego.

    2. Wygeneruj raport, który tworzy etykiety adresowe. (Podpowiedź: pracę rozpocznij od utworzenia formularza QuickReport Labels znajdującego się na stronie Forms Repozytorium).

    3. Zmodyfikuj raport stworzony w tym rozdziale tak, aby imię i nazwisko pracownika (pola FirstName i LastName) były wyświetlane przez komponent QRExpr.

    4. Przeczytaj plik DEPLOY.TXT znajdujący się w głównym katalogu Delphi aby zrozumieć, z czym wiąże się dystrybucja bazodanowej aplikacji Delphi.

    728 Część III

    728 C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\18.doc

    C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\18.doc 705

    Rozdział 18. Tworzenie aplikacji bazodanowych 727



    Wyszukiwarka

    Podobne podstrony:
    Aleksey Tolstoy [Ordeal] 18 (doc)
    Åwiczenie 18 doc
    ANTYK (18) DOC
    Dyrektywa 99 11 EEC o GLP zmieniająca 87 18 doc
    tabela (18) doc
    a (18) doc
    18 1 (2) DOC
    DYSKI 18 DOC
    Ps doskonalenie przyjęcia kl 5 bc 18 doc
    b (18) doc
    Ćwiczenie 18 doc
    25 (18) doc
    Ćwiczenia nr 18 doc
    72 (18) DOC
    Sylwia poprawa 18 doc
    Zestaw 18 doc
    PROJEKT (18) doc
    18 1 (3) DOC

    więcej podobnych podstron