You are on page 1of 89

MAREK BISZ

MIROSAW CIELECKI

WICZENIA
Z
C++BUILDERA
-.'

Spis treci
Wstp.......................................................................................................................................5
1. Podstawy programowania w Windows i C++ Builderze...............................................6
2. Biblioteka VCL - przegl/d najwa1niejszych komponentw........................................20
3. Programowanie obiektowe w C++ Builderze.................................................................41
4. Operacje wejcia-wyjcia...............................................................................................58
5. Grafika i multimedia w C++ Builderze..........................................................................66
6. Bazy danych w C++ Builderze.......................................................................................75
7. Pliki pomocy Windows, DDE, modu= LZExpand.........................................................101
Wstp
Niniejsza ksi/1ka ma za zadanie unaoczni@ czytelnikowi prostot dzisiejszego
programowania w Windows. Narzdziem, ktre w niej zosta=o omwione jest pakiet
firmy Borland - C++ Builder. W trakcie lektury czytelnik przejdzie kilka etapw -
pocz/wszy od prostej aplikacji, skoAczywszy na zagadnieniach zwi/zanych z dyna-
miczn/ wymian/ danych midzy procesami.

Zgodnie z zasad/, i1 jeden przyk=ad jest wart wicej ni1 1000 s=w, ka1de z oma-
wianych zagadnieA zrealizowane jest wed=ug schematu: wprowadzenie - kolejne za-
dania wnioski i podsumowanie. Gotowe projekty zadaA znajdzie czytelnik na dys-
kietce po brzegi wypchanej interesuj/cymi przyk=adami i do=/czonej do ksi/1ki. Pole-
cam dok=adne przeledzenie wszystkich zadaA (w=asnorczne wpisywanie kodu
Erd=owego cho@ bardziej dowiadczonym wystarczy by@ mo1e sama jego analiza).

Przed jednym chcia=bym przestrzec potencjalnego czytelnika, ktry by@ mo1e
oczekuje, i1 zostanie poprowadzony od kliknicia do kliknicia - prost/ drog/ stan-
dardowych aplikacji: nie jest to typowa ksi/1ka dla pocz/tkuj/cych. Nie bdziemy
w niej chadza@ utartymi cie1kami. Od pocz/tkuj/cego czytelnika wymaga ona zwik-
szonego wysi=ku. Nie ma tu konkretnych przepisw w rodzaju jak upiec ciasto mali-
nowe", Za=o1eniem tej ksi/1ki jest wskazanie pewnych drg jako materia=u do w=a-
snych eksperymentw.

Czytelnikowi nie stawia si 1adnych wymagaA. Przyda=aby si jedynie podstawo-
wa znajomo@ jzyka C(C++), ale i to nie jest konieczne. Staram si poni1ej wyjania@
potrzebne konstrukcje jzyka, wiele mo1na rwnie1 nauczy@ si z za=/czonych do
ksi/1ki przyk=adw. Przyda si te1 znajomo@ (cho@by podstawowa) jzyka angiel-
skiego, gdy1 dokumentacja on lin" dla C++ Buildera nie zosta=a na razie spolszczona.

Chcia=bym w tym miejscu zaznaczy@, 1e ksi/1ka ta w 1adnym wypadku nie obej-
muje wszystkich zagadnieA zwi/zanych z programowaniem w C++ Builderze. Ma ona
jedynie za zadanie umo1liwi@ czytelnikowi =atwe i szybkie wejcie w bogaty wiat
programowania w Windows.

1. Podstawy programowania w Windows i C++
Builderze
wiczenie 1.1. Ilustracja sensu uywania procedur, inaczej: nis wilk razy kilka, ponieli
i wilka..."
Wprowadzenie: wszyscy, ktrzy zaczynaj/ programowa@, robi/ to w przestarza=ym stylu i
zak=adaj/, 1e wszystko odbywa@ si musi w z gry okrelonym porz/dku.
Program .sekwencyjny sk=ada si z nastpuj/cych po sobie instrukcji wykonywanych w
sztywnej kolejnoci. W poni1szych zadaniach udowodnimy, 1e z odejcia od sekwencyjnego
stylu programowania wyp=ywaj/ pewne wymierne korzyci.
Zadanie 1.1.1: pos=uguj/c si liniami komentarza napisa@ program wywietlaj/cy trzy razy
okienko z napisem Witaj".
Wykonanie:
// utworzenie okna
// wywietlenie napisu Witaj"
// zamknicie okna
// utworzenie okna
// wywi et l eni e napi su Wi t aj "
// zamkni ci e okna
// utworzenie okna
// wywi et l eni e napi su Wi t aj "
// zamkni ci e okna
Podsumowanie: ju1 na pierwszy rzut oka wida@, 1e co jest nie tak. Nasz program" wykona
swoje - wypisze trzy razy na ekranie napis Witaj", ale brakuje temu wszystkiemu elegancji.
Gdybymy chcieli zmieni@ napis Witaj" na jaki inny, musielibymy to zrobi@ trzykrotnie!
Poza tym nasz program sk=ada si z trzech identycznych czci. W ka1dej z nich moglibymy
si pomyli@. Wszystkie te wady programowania sekwencyjnego rozwi/zuje u1ycie procedur.
Zadanie 1.1.2: pos=uguj/c si liniami komentarza napisa@ szkielet" programu wy-
wietlaj/cego trzy razy okienko Witaj" przy u1yciu procedury wyswietl_okno.
Wykonanie:
// procedura wyswietl_okno
// C
// utworzenie okna
// wywietlenie napisu Witaj"
// zamknicie okna
// ]
// program g$wny
// C
// wyswietl__okno (pierwsze wywo$anie procedury)
// wyswietl_okno (drugie wywo$anie procedury)
// wyswietl_okno (trzecie wywo$anie procedury)
// )
Podsumowanie: dzisiaj zmiana s=owa Witaj" na inne wymaga ingerencji tylko w jednym
miejscu programu. Nasz przyk=adowy algorytm jest prosty, ale wyobraEmy sobie, 1e
mielibymy poprawia@ du1y program zawieraj/cy dziesi/tki tysicy linii kodu.... Nastpna
sprawa - gdyby si okaza=o, 1e w naszej procedurze trzeba co zmieni@ (np. dopisa@ pobranie
odpowiedzi od u1ytkownika) - wystarczy do tego jedna zmiana wewn/trz procedury, a nie
trzy kolejne identyczne, jak w przypadku zadania 1.1.1.
Zadanie 1.1.3: napisa@ program wywietlaj/cy kolejno ,,Witaj", Witaj ponownie", Witaj raz
jeszcze".
Wykonanie:
// procedura wyswietl_okno ($a(cuch_znakw tekst_do__wyswietlenia)
// C
// utworzenie okna
// wywietlenie napisu tekst_do_wyswietlenia
// zamknicie okna
// }
// program g$wny
// t
// wyswietl_okno(,.Witaj") (pierwsze wywo$anie procedury)
// wyswietl_okno(Witaj ponownie") (drugie wywo$anie procedury)
// wyswietl_okno(Witaj raz jeszcze") (trzecie wywo$anie procedury)
// }
Podsumowanie: jak wida@, to 1e pos=u1ylimy si procedur/, w niczym nie ogranicza
naszych mo1liwoci, Przy odpowiedniej jej modyfikacji (dodalimy parametr
tekst_do_wyswietlenia) mamy w dalszym ci/gu mo1liwo@ wywietlenia za ka1dym razem
innego tekstu. Na uwag zas=uguje fakt, 1e tekst dowyswietlenia jest jedynie identyfikatorem
tekstu przesy=anego do procedury z programu g=wnego. Tak wic
przy pierwszym wywo=aniu naszej procedury zawartoci/ tekst_do_wyswietlenia bdzie
Witaj", w drugim - Witaj ponownie" itd. Tym sposobem ta sama procedura mo1e
wywietla@ za ka1dym razem inny tekst, cho@ przecie1 nie zmieniamy jej kodu.
Taki identyfikator" bdziemy dalej nazywa@ zmienn/. Dla ka1dej zmiennej musi zosta@
okrelony typ danych, ktre ma zawiera@ (dlatego w deklaracji procedury u1ylimy okrelenia
(a)cuch_znakw).
wiczenie 1.2. Funkcje - poszerzenie informacji z wiczenia 1.1
Wprowadzenie: funkcja jest odpowiednikiem procedury, ale zwraca warto@. W C++ mamy
do czynienia w=anie z funkcjami.
Zadanie 1.2.1: przekszta=ci@ nasz program przyk=adowy z zadania 1.1.3, tak aby pobiera= ort
od u1ytkownika odpowiedE na nasze pozdrowienie lub pytanie.
Wykonanie:
//$a(cuch__ znakw wyswietl^okno ($a(cuctwnakw tekst_do_wysw1etleni a)
// {
// $a(cuctwnakw Odpowiedz
// utworzenie okna
// wywietlenie napisu tekst_do_wyswietlenia
// pobranie odpowiedzi u1ytkownika i wstawienie jej pod zmienn2
// Odpowiedz
// zamknicie okna
// zwrot zmiennej Odpowiedz
// }
// program g$wny
// {
// $a(cuch_znakw warto3_zwrcona
// warto3_zwrcona - wyswietl_okno(Witaj") (pierwsze wywo$anie
// funkcji)
// warto3_zwrcona=wyswietl_okno(warto3_zwrcona) (drugie wywo$anie
// funkcji)
// warto3_zwrcona= wyswietl_okno(warto"3_2wrcona) (trzecie wywo$anie
// funkcji )
// }
Podsumowanie: jak wida@ program bawi si z u1ytkownikiem w papug": za ka1dym razem
wywietla to, co zosta=o poprzednio wpisane. Warto zauwa1y@, 1e w deklaracji naszej funkcji
znajduje si okrelenie typu zwracanych danych. Tak wic przy pierwszym wywo=aniu
funkcji z programu g=wnego zostanie zwrcony =aAcuch znakw i podstawiony pod zmienn/
warto./_zwrcona. Zauwa1my, 1e przy nastpnym wywo=aniu funkcji jako parametr do
wywietlenia przesy=ana jest w=anie owa zmienna (czyli to. co wprowadzi= u1ytkownik). W
drugim wywo=aniu funkcja wywietli zawarto@ tej zmiennej it
wiczenie 1.3. Zdarzenia i ich obsuga, czyli wszystko si% moe zdarzy'"
Wprowadzenie: system Windows zmieni= podejcie do zasad sterowania programem.
Dawniej wszystko opiera=o si o wykonywanie jednej ustalonej sekwencji operacji. W
Windows wszystko wi/1e si z odpowiadaniem na zdarzenia. Na przyk=ad gdy klikniemy
mysz/ w odpowiednim miejscu, rozwinie si menu - oznacza to, 1e aplikacja odpowiedzia=a
na zdarzenie o nazwie mouseclick. Programowanie w Windows jest zorientowane przez
zdarzenia. Musimy by@ przygotowani na wszystko, co mo1e zrobi@ u1ytkownik. W zadaniu
poni1ej czeka nas pierwszy kontakt z IDE - czyli rodowiskiem C++ Buildcra, oraz prosty
program odpowiadaj/cy na kliknicie mysz/.
Po uruchomieniu C++ Buildcra, widzimy przed sob/ co mniej wicej takiego:
Cz@ A-B to trzon" pakietu. Znajduje si tutaj podrczne menu u1ytkownika, z lewej strony
kilka przyciskw operacyjnych oraz (rzecz wa1na) w czci B - zestaw komponentw, czyli
klockw", z ktrych bdziemy budowa@ aplikacje. Komponenty s/ specjalnie
zaprojektowanymi obiektami jzyka C++ (lub Pascal). Zbudowane s/ tak, aby =atwe by=o ich
wielokrotne u1ycie. W dalszej czci ksi/1ki komponenty zostan/ jeszcze omwione
dok=adniej.
Cz@ C to Inspektor Obiektw - bardzo przydatne narzdzie s=u1/ce do edycji w=aciwoci
obiektw (komponentw), a tak1e przypisywania im okrelonych reakcji na

zdarzenia. Na przyk=ad reakcja na kliknicie mysz/ nosi nazw OnClick". Zestaw
obs=ugiwanych przez dany obiekt zdarzeA jest widoczny na karcie Events (zdarzenia)
Inspektora Obiektw.
Wczci D widzimy g=wne okno naszej przysz=ej aplikacji.
Zadanie 1.3.1: napisa@ program odpowiadaj/cy na kliknicie mysz/ w dowolnym miejscu
formularza (czyli naszego okna g=wnego) wywietleniem okienka z wiadomoci/.
Wykonanie:
1. WInspektorze Obiektw wybieramy kart Events (zdarzenia).
2. Dwukrotnie klikamy w bia=e pole obok pozycji OnClick. Pojawi si okno edycji kodu.
Teraz midzy klamrami {" i }" oznaczaj/cymi cia=o funkcji wpiszemy kod odpowiedzi
na zdarzenie mouseclick (rysunek 1.3)
.
3. Wpisujemy ,M^^sageBox(0,"Hej to ja!","Uhaha",0);" (w porwnaniu z przyk=adami z
poprzednich @wiczeA nie jest to mo1e zbyt jasne - ale taka jest cena przeniesienia teorii w
praktyk). Jeszcze jedna uwaga - uwa1ajmy na du1e i ma=e litery. Je1eli wpiszemy
messagebox\ C++ Builder nie rozpozna tego jako nazwy funkcji wywietlaj/cej okienko
(MessageBox).

4. Pozostaje nam skompilowa@ i uruchomi@ nasz program. Kompilacja jest procesem
t=umaczenia wpisanego przez nas (lub wygenerowanego automatycznie) kodu Erd=owego
do postaci bezporednio zrozumia=ej dla maszyny. Pod C++ Builderem mo1emy to
wykona@ za pomoc/ klawisza skrtu (F9") lub wybieraj/c z menu Run pozycj Run.
5. Jeli wszystko zrobilimy poprawnie, powinno pojawi@ si okno z napisem Forml".
Kliknijmy w obszar tego okna. Pojawi=o si mniejsze okienko Uhaha" z napisem Hej to
ja!" - nasza aplikacja odpowiedzia=a na nacinicie przycisku myszy wykonaniem
wpisanego przez nas kodu, czyli utworzeniem okienka dialogowego.
6. Nale1a=oby teraz zapisa@ nasze dokonania. Zamknijmy aplikacj (wystarczy nacisn/@
znak X" w prawym grnym rogu okna aplikacji). Po powrocie do C++ Bu-ildera
wybieramy z menu File opcj Save Project As ...".
7. W tym miejscu umwimy si co do konwencji nazw, jakich bdziemy u1ywa@. Porz/dek
wymaga, aby kolejne zadania zapisywa@ w osobnych katalogach. Umwmy si, 1e nazwy
katalogw (folderw) bdziemy zawsze tworzy@ wed=ug schematu zad" + nr zadania.
Nazw pliku projektu i programu czytelnik mo1e nada@ wed=ug uznania, cho@ mo1na
zostawi@ po prostu Projectl" i Unitl".
8. W pierwszym oknie, ktre si pojawi, nacinijmy prawy klawisz myszy, a nastpnie
wybierzmy kolejno Nowy obiekt" i Folder". W nazw nowego foldera wpiszmy zad
131", a nastpnie wejdEmy do niego (dwukrotne kliknicie mysz/)
i nacinijmy Zapisz". Pojawi si nastpne okno z pytaniem o nazw projektu -
wciskamy Zapisz".
Podsumowanie: nauczylis'my si, jak skompilowa@ i zapisa@ prosty projekt w C++
Builderze. Co wa1niejsze - nauczylis'my si, jak obs=ugiwa@ zdarzenia Windows. Pod
Windows wystpuj/ dziesi/tki, a raczej setki r1nego typu zdarzeA. Nie wszystkie z nich bd/
nas w danym momencie interesowa@, jednak warto zda@ sobie spraw z tego, 1e to w=anie
zdarzenia bd/ orodkiem naszych programw. Ka1dy nasz program, jak bardzo nie by=by
skomplikowany - bdzie jedynie zbiorem odpowiedzi na szereg r1norodnych zdarzeA.
Odwrotnie ni1 w programowaniu sekwencyjnym -u nas u1ytkownik przejmie inicjatyw w
swoje rce i to on bdzie decydowa=, jakie operacje w danym momencie wykonywa@.
wiczenie 1.4. Waciwoci i metody zwi*zane z obiektami C++ Builder
Wprowadzenie: w dalszej czci ksi/1ki bardzo czsto bdziemy pos=ugiwa@ si pojciem
obiektu, jego w=aciwoci (atrybutw) i metod (czyli mo1liwych do wykonania na nim
operacji). Mam nadziej, 1e pojcie obiektu stanie si jasne w trakcie wykonywania kolejnych
@wiczeA. Wstpnie mo1emy zdefiniowa@ obiekt jako odpowiednik dowolnego rzeczownika w
jzyku ludzi. Je1eli chcemy przygotowa@ fili1ank kawy, obiektami naszej czynnoci bd/
=y1eczka, cukier, fili1anka, kawa itp. Musimy teraz wykona@ na naszych obiektach
odpowiednie operacje, a wic wsypa@ kaw do fili1anki, =y1eczk/ nabra@ cukru, zala@ kaw
wrz/tkiem itd. Operacje wykonywane na obiektach bdziemy dalej nazywa@ ich metodami
lub funkcjami. Odpowiednikiem okrelenia metoda" w jzyku ludzi jest dowolny czasownik.
W poni1szych zadaniach nauczymy si, jak zmienia@ atrybuty obiektw (ich w=aciwoci)
oraz w jaki sposb wykonywa@ na nich r1norodne operacje (ich metody).
Zadanie 1.4.1: zmodyfikowa@ program z zadania 1.3.1, tak aby Formularz g=wny mia= w
nag=wku napis Test w=aciwoci".
Wykonanie:
1. Musimy najpierw odczyta@ projekt z zadania 1.3.1. Z menu File wybieramy opcj Open
Project". Je1eli dla naszego projektu wybralimy zalecan/ nazw, to w powsta=ym
okienku wybieramy folder "zadl31" i plik projektu Project 1.mak" (mo1emy klikn/@
dwukrotnie lub zaznaczy@ i nacisn/@ przycisk Otwrz").
2. Zabierzemy si teraz za zmian nag=wka naszego formularza. Mo1emy go zmieni@
wykorzystuj/c w tym celu Inspektora Obiektw. W polu edycji tu1 pod nag=wkiem
Object Inspector" widzimy napis Forml:TForml" - oznacza on, 1e aktywnym
obiektem, ktrego dotycz/ wszystkie inne wywietlane przez Inspektora dane, jest w=anie
nasze okienko g=wne aplikacji - czyli Forml". Poni1ej znajdziemy midzy innymi
w=aciwo@ (property) Caption - czyli nag=wek, tytu=, a zaraz obok napis Forml". Nie
trzeba chyba podpowiada@, 1e to w=anie tutaj musimy dokona@ zmian dotycz/cych tekstu
nag=wka. Wpiszmy Test w=aciwoci". Nale1y w tym miejscu zaznaczy@, 1e
zmienilimy jedynie tekst nag=wka, a nie nazw samego formularza. Tak wic formularz
o nazwie Forml" posiada nag=wek Test w=aciwoci". Gdyby chodzi=o nam o zmian
nazwy formularza, musielibymy pos=u1y@ si inn/ w=aciwoci/- Name.
3. Jeli poprawnie zmienilimy w=aciwo@ Caption w Inspektorze Obiektw, to efekty tego
powinny pojawi@ si bezzw=ocznie w nag=wku naszego formularza.
4. Zapiszmy teraz nasz projekt i plik programu w folderze o nazwie zgodnej "z przyjt/
konwencj/, czyli zad 141" (patrz zadanie 1.3.1 p. 6).
Podsumowanie: nauczylimy si, jak za pomoc/ Inspektora Obiektw zmienia@ atrybuty
obiektw. Zauwa1ylimy na pewno, 1e obiekty posiadaj/ szereg r1norodnych
w=aciwoci, za pomoc/ ktrych mo1emy wp=ywa@ na ich wygl/d, rozmiar itp. Mo1-
liwoci naszej ingerencji w atrybuty obiektw nie ograniczaj/ si tylko do wykorzystania
Inspektora Obiektw. Mo1emy zmienia@ je rwnie1 bezporednio w kodzie programu.
Zostanie to wyjanione w nastpnym zadaniu, w ktrym zapoznamy si rwnie1 ze
sposobami wykonywania na obiektach r1norodnych operacji (uruchamiania ich metod).
Zadanie 1.4.2: do programu z zadania 1.4.1 doda@ przycisk. Nacinicie przycisku ma
powodowa@ zmian koloru formularza na niebieski. Zmieni@ funkcj FormClick"
(zdarzenie OnClick dla formularza), tak aby powodowa=a na przemian pojawianie si i
znikanie tego przycisku.
Wykonanie:
1. Z menu File wybieramy opcj Open Project" i otwieramy projekt z katalogu zadl41".
2. Aby doda@ przycisk w polu formularza z palety komponentw (cz@ B z rysunku 1.1)
wybieramy kart Standard, a nastpnie komponent szsty z lewej (czyli But-ton -
przycisk). Teraz jeszcze kliknicie w obrbie naszego formularza i ju1 jest -mamy drugi
obiekt w projekcie.
3. Spjrzmy na Inspektora Obiektw - identyfikator naszego obiektu to Buttonl", poni1ej
widzimy jego atrybuty, takie jak Caption (czy domylamy si, co on oznacza?).
4. Zaprojektujmy teraz reakcj naszego przycisku na wcinicie klawisza myszy. Jak
robilimy to poprzednio w wypadku formularza, tak i teraz wybieramy kart Events
Inspektora Obiektw i klikamy podwjnie na bia=e pole obok pozycji OnClick". Wcia=o
funkcji wpisujemy ,fotml->Color=clBlue;". Znaki ->" oznaczaj/ operator selekcji
(wskazania). Z jego pomoc/ wskazujemy maszynie, i1 chodzi nam o w=aciwo@ Color
obiektu Forml. Znak =" oznacza przypisanie -komputer oblicza wyra1enie po prawej
stronie operatora (cIBlue", czyli kod koloru niebieskiego) i wstawia je nastpnie w
wyra1enie po lewej stronie (Forml->CoIor", czyli w=aciwo@ okrelaj/ca kolor
naszego formularza).

5. Po=ow zadania mamy ju1 za sob/ - po naciniciu przycisku zostanie uruchomiona
funkcja, ktra zmieni kolor formularza. Pozosta=a nam modyfikacja funkcji zwi/zanej z
klikniciem w formularz - FormCIick". Aby dokona@ edycji tej funkcji, najwygodniej
bdzie zmieni@ aktualny obiekt w Inspektorze Obiektw na Forml (wystarczy klikn/@
gdziekolwiek w obszar formularza), a nastpnie wybra@ OnClick" z karty Events.
6. Kliknijmy w obszarze formularza. Napis w Inspektorze Obiektw zmieni= si na
Forml::TForml". Oznacza to, 1e aktywnym obiektem, ktrego atrybuty wywietla
Inspektor Obiektw, jest znw nasz formularz g=wny.
7. Wybieramy kart Events Inspektora Obiektw, a nastpnie klikamy podwjnie w napis
FormCIick" obok pozycji OnClick". Po prawej pojawi si pole edycji gotowe do
przyjcia poprawek. Wy=/czamy instrukcj ,J\dessageBox(0 "Hej to ja!","Uhaha",0);".
Dodajemy z przodu dwa znaki ,//". Spowoduj/ one, i1 kompilator t=umacz/c kod
Erd=owy pominie wszystko, co znajdzie si za nimi a1 do koAca wiersza. Jest to bardzo
przydatne cho@by przy wpisywaniu komentarzy do tekstu programu (aby uczyni@ go
bardziej czytelnym).
8. Poni1ej wpiszmy teraz:
// jeli w$aciwo3 Visib1e" (widzialny) obiektu Buttonl jest rwna
// () true (czyli prawda") - wykonuj komend poni1ej
if (Buttonl->Visib1e==true) // warunek
Buttonl->Hide(); // komenda wykonywana przy spe$nionym warunku
else // w innym wypadku...
Buttonl->Show(); // komenda wykonywana, jeli warunek nie
// jest spe$niony
9. Oczywicie wszystko, co znajduje si po znakach //" mo1emy pomin/@..
.
10. Zapiszemy teraz projekt i program w kolejnym katalogu (zadl42") i mo1emy
uruchamia@ program w celu sprawdzenia efektw jego dzia=ania (F9").
Podsumowanie: dowiedzielimy si, jak w sposb dynamiczny (podczas dzia=ania programu)
wp=ywa@ na w=aciwoci obiektw. Poznalimy konstrukcj wykonania warunkowego (if (...)
else (...)). Potrafimy ju1 tak1e wykonywa@ na obiektach pewne operacje (metoda ,,Buttonl-
>Hide()" chowa" przycisk, za ,,Buttonl->Show()" powoduje, i1 staje si on z powrotem
widzialny).
Warto zauwa1y@, 1e zdarzenia s/ przechwytywane" przez obiekty, ktrych dotycz/.
Nacinicie klawisza myszy nad przyciskiem powodowa=o tylko jego odpowiedE, pomimo
tego, 1e obszar ten nale1y rwnie1 do formularza.
wiczenie 1.5. Pierwsza aplikacja (podstawy pracy ze rodowiskiem)
Wprowadzenie: na pocz/tek zbudujemy sobie odtwarzacz do plikw animacji *.avi.
Wystarcz/ do tego trzy linie rcznie wpisanego kodu. Tak! To nie 1art! Zeby zbudowa@ pod
C++ Builderem prost/ aplikacj, nie potrzeba ani wiele wiadomoci, ani du1ego wysi=ku.
Zadanie 1.5.1: zbudowa@ odtwarzacz plikw animacji (*.avi).
Wykonanie:
1. Z menu File wybieramy opcj New....
2. Wybieramy ikon Application z karty New.
3. Zmieniamy tekst w nag=wku formularza na Odtwarzacz" (Inspektor Obiektw -
w=aciwo@ Caption - patrz zadanie 1.4.1).
4. Niestety - sam napis w nag=wku nie za=atwi nam odtwarzania plikw *.avi. Po-
trzebujemy komponent (obiekt), ktry odwali" za nas ca=/ czarn/ robot - czyli
wywietlanie kolejnych klatek filmu, odtwarzanie dEwiku itd. C++ Builder taki
komponent posiada. Znajduje si on na palecie komponentw (karta System, sidmy z
kolei - MediaPlayer). Klikamy na niego mysz/, nastpnie jeszcze raz na nasz formularz -
i ju1 jest - mamy drugi obiekt w naszym projekcie. Z Inspektora Obiektw mo1emy bez
trudu odczyta@, 1e nazywa si on MediaPlayerl - i tej nazwy bdziemy u1ywa@ do jego
identyfikacji. Wida@, 1e posiada on interfejs bardzo podobny do zwyk=ego odtwarzacza -
szereg przyciskw s=u1/cych r1nym funkcjom. Midzy innymi pierwszy z lewej, ten,
ktry nas interesuje najbardziej -odtwarzanie.
5. Jednak1e samo odtwarzanie to nie wszystko. Przyda=oby si, 1eby u1ytkownik mg= nam
wskaza@, gdzie znajduje si plik, ktry program ma odtworzy@. WWindows s=u1/ do tego
specjalne okienka dialogowe. Jedno z nich bdzie nam potrzebne. Z karty Dialogs
wybieramy komponent pierwszy z brzegu - OpenDialog i tak jak poprzednio klikamy na
nasz formularz. Pojawia si ma=y kwadracik, ktry symbolizowa= bdzie okienko
dialogowe. Rzut oka na Inspektora Obiektw -jego nazwa (identyfikator) to OpenDialogl
(zaczynamy ju1 chyba rozumie@ zasady tworzenia nazw domylnych przez C++
Buildera).
6. Dope=nieniem kompletu listy potrzebnych obiektw bdzie przycisk, czyli Button (szsty
na karcie Standard). Chcemy, aby w momencie, w ktrym u1ytkownik nacinie nasz
przycisk Buttonl, otwiera=o si okienko dialogowe OpenDialogl pozwalaj/c
u1ytkownikowi na wybr pliku do odtwarzania przez MediaPlayerl.
7. Musimy teraz jeszcze zmodyfikowa@ OpenDialogl, tak aby wywietla= tylko te pliki,
ktre nas interesuj/ - czyli *.avi. Mo1na to wykona@ modyfikuj/c w=aciwo@ Filter, czyli
mask plikw. Najpierw zaznaczymy nasz OpenDialogl klikaj/c na niego. Inspektor
Obiektw bdzie teraz wywietla@ jego w=aciwoci.
8. W Inspektorze Obiektw odszukujemy pozycj Filter. Obok nie ma jednak zwy-
czajnego pola edycji, w ktre mo1na by wpisa@ mask pliku. Jest to w=aciwo@ nieco
bardziej skomplikowana - sk=ada si z etykiety maski i jej w=aciwej wartoci. Kliknijmy
w ma=y przycisk z czterema kropkami - pojawi si specjalne okienko dialogowe.

9. W pierwsz/ pozycj w polu Filter name wpisujemy Animacje (*.avi)". Obok w polu
Filter - wpisujemy *.avi". Naciskamy OK - i to ju1 wszystko. Od tej pory nasz
OpenDialogl bdzie wywietla= tylko pliki *.avi
.
10. Zajmiemy si teraz stron/ operacyjn/" ca=ej aplikacji - a wic co si ma wydarzy@,
gdy...". Chcemy, aby program reagowa= na nacinicie przycisku Buttonl otwarciem
okienka dialogowego OpenDialogl i otwarciem wybranego pliku do odtwarzania przez
MediaPlayerl. Jeli uda nam si zakodowa@ to, o czym powiedzielimy przed chwil/ w
formie zrozumia=ej dla C++ Buildera - to otrzymamy gotow/ aplikacj!
11. Jedn/ z metod obiektu OpenDialogl jest Execute(), czyli Wykonaj" - oznacza to ni
mniej ni wicej tylko otwarcie okna dialogowego. Lista metod zwi/zanych z danym
komponentem jest dostpna poprzez zaznaczenie go (kliknicie), nacinicie FI", a
nastpnie wybr z listwy grnej podwietlonego s=owa Methods". Nie przera1ajcie si
liczb/ w=asnoci i metod zwi/zanych z obiektami. Nawet jeli nie znacie angielskiego,
warto korzysta/ z pliku pomocy kontekstowej, cho/by po to , aby przeglEdnE/ listF
w(asno.ci i metod zwiEzanych z obiektami VCL.
12. Wybieramy obiekt Buttonl. Chcemy, aby reagowa= on na kliknicie otwarciem
OpenDialogl. W Inspektorze Obiektw wybieramy wic kart Events, a nastpnie
klikamy dwukrotnie w bia=e pole obok zdarzenia OnClick.
13. Po prawej stronie otworzy nam si edytor gotowy do wpisania kodu. Wpiszemy tam trzy
linie, ktrych znaczenie bdziemy sobie rwnoczenie wyjania@.
14. W pierwszym rzdzie chodzi nam o uruchomienie (otwarcie) OpenDialogl. Wpisujemy
wic wywo=anie metody (funkcji), ktra to wykona:
OpenDialogl->Execute();
Polecenia jzyka C++ koAczymy rednikiem. Puste nawiasy obok Execute oznaczaj/, i1
metoda ta nie wymaga 1adnych parametrw. Jej wykonanie spowoduje otwarcie okna
dialogowego i ustawienie w=aciwoci FileName na plik, ktry wybierze u1ytkownik.
15. Musimy jeszcze teraz da@ o tym zna@ MediaPlayerl. Wpiszmy poni1ej:
MediaPlayerl->FileName=0penDi alogl->FileName;
16. Teraz rozkazujemy" obiektowi MediaPlayerl, 1eby otworzy= nasz plik:
Media PIayerl->Open();
Ostatecznie funkcja bdzie wygl/da@ tak:
void __fastcall TForml::ButtonlClickCTObject *Sender)
}
OpenDialogl->Execute();
MediaPlayerl->Fi leName=OpenDialogl->FileName;
MediaPlayerl->Open();
}
17. I to ju1 wszystko. Reszta, czyli nacinicie play", nale1e@ bdzie do u1ytkownika.
18. Teraz jeszcze zapiszemy (folder zadl51"), uruchomimy aplikacj (naciskamy F9") i
gotowe!
19. Jeli s/ problemy ze znalezieniem plikw *.avi (prosz si nie mia@ - sam mia=em
k=opoty...), nale1y poszuka@ w katalogu Windows\Help lub jeszcze lepiej -
FunstuffWideos na CD instalacyjnym Win95. Zycz dobrej zabawy.
Podsumowanie: w ci/gu kilku pierwszych chwil spdzonych z C++ Builderem napisalimy
w pe=ni funkcjonaln/ aplikacj! Co wicej wymaga=a ona jedynie trzech rcznie
wprowadzonych linii kodu! Tu w=anie tkwi si=a C++ Buildera - zestaw gotowych
wyspecjalizowanych komponentw oraz elastyczne narzdzia do operowania na nich
zwalniaj/ nas z troski o szereg skomplikowanych elementw interfejsu Windows. Pozostaje
nam jedynie sedno problemu", czyli sam algorytm programu. Interfejs u1ytkownika
za=atwiaj/ za nas obiekty zaprojektowane przez specjalistw firmy Borland. Przy czym wa1ne
jest, 1e lista komponentw nie jest zamknita. Jeli zaprojektujemy obiekt, co do ktrego
mamy podejrzenie, 1e nadaje si do powtrnego wykorzystania, mo1emy po lekkiej
modyfikacji do=/czy@ go do biblioteki komponentw. W dalszej czci ksi/1ki zostanie to omwione
dok=adniej.
W tym miejscu chcia=bym sprowadzi@ nieco na ziemi ogarnitych na pewno eufori/ wie1o
upieczonych programistw. Rzeczywisto@, Panie i Panowie, jest nieco bardziej przyziemna.
]le (lub niedok=adnie ) zaprojektowana aplikacja potrafi p=ata@ nam r1ne figle. Zanim wic
pobiegniecie pochwali@ si znajomym swoim osi/gniciem, przejdEmy do zadania 1.5.2 i
sprbujmy przyjrze@ si bli1ej naszemu programowi.
Zadanie 1.5.2: przetestowa@ program z zadania 1.5.1 przy u1yciu wewntrznego debuggera
C++ Buildera.
Wykonanie:
1. WyjdEmy z okna Otwrz nie poprzez wybranie pliku, a przez anuluj". Co si dzieje?
Aplikacja zg=osi=a b=/d! MediaPlayerl nie mg= otworzy@ pliku. Dlaczego? C1, przed
takim pytaniem staniecie nieraz projektuj/c programy. W takich sytuacjach wyjcia s/
trzy: zrezygnowa@ (w naszym wypadku odpada), zacz/@ od nowa (w naszym wypadku
niczego nie rozwi/zuje) i wreszcie - przeledzi@, co si w=aciwie dzieje.
2. Pod C++ Builderem dostpny jest specjalnie do tego celu przeznaczony debugger, czyli
odpluskwiacz". Tym razem zanim uruchomimy aplikacj, za=o1ymy tzw. pu=apk. Jest to
miejsce, w ktrym program bdzie przerywa= swoje wykonanie,aby da@ nam okazj do
dok=adnego przyjrzenia si algorytmowi. W oknie edycji podjedEmy kursorem pod lini
,,OpenDialogl->Execute();" i nacinijmy F5". Pojawi= si czerwony pasek? Jeli nie,
oznacza to, 1e macie inne ni1 domylne ustawienia skrtw klawiaturowych. Mo1na
wtedy nacisn/@ prawy klawisz myszy i wybra@ Toggle Breakpoint (prze=/cz punkt
przerwania). Innym sposobem ustawienia pu=apki" jest kliknicie mysz/ na lewym
marginesie. Od tej pory program bdzie si zawsze zatrzymywa= w tym miejscu, aby
umo1liwi@ nam analiz kodu.

3. Uruchamiamy aplikacj (F9). Wydawa=oby si, 1e wszystko przebiega normalnie
- pojawi=o si okienko i program oczekuje na akcj z naszej strony. Nacinijmy wic
przycisk Buttonl. Program przeszed= do okna edycji! Na tym w=anie polega praca
krokowa. Po ka1dej linii kodu C++ Builder bdzie przerywa@ wykonanie programu
podwietlaj/c instrukcje, ktre maj/ by@ wykonane w nastpnym kroku. Podwietlenie
znajduje si na linii ,,OpenDiaIogl->Execute()" - C++ Builder rozpoczyna realizacj
odpowiedzi przycisku na kliknicie mysz/. W nastpnym kroku wykona si (otworzy)
okienko OpenDialogl.
4. F8"(nastpny krok) - i widzimy, 1e tak jest rzeczywicie: pojawi=o si okienko
dialogowe. Nacinijmy tak jak poprzednio Anuluj". Podwietlenie jest teraz na linii
MediaPlayerl->FileName=OpenDi alogl->FileName;
5. Tu zatrzymamy si nieco d=u1ej. Jest to instrukcja podstawienia komputer oblicza
wyra1enie po prawej stronie i wstawia obliczon/ warto@ po stronie lewej. W naszym
wypadku powinien nazw pliku z OpenDialogl wstawi@ w nazw pliku z MediaPlayerl.
Zaraz, zaraz -jak/ nazw pliku? Przecie1 nic nie wybralimy
- przyjrzyjmy si temu bli1ej.
6. PodjedEmy kursorem pod OpenDialogl->FileName", nacinijmy prawy klawisz myszy i
wybierzmy pozycj Evaluate/Modify.
7. Pojawi si okienko Evaluate/Modify", czyli podgl/d i modyfikacja. Chodzi oczywicie
o podgl/d obiektw i zmiennych. Nam zale1y na podgl/dniciu w=aciwoci FileName
obiektu OpenDialogl. Wpiszmy wic w pole oznaczone etykiet/ Expression (wyra1enie):
OpenDialogl->FileName" <ENTER>.
8. W polu poni1ej pojawi si zawarto@ w=aciwoci FileName obiektu. Co widzimy? Tylko
dwa apostrofy. Oznacza to, 1e sk=adowa Filename jest pusta. To by=o zreszt/ do
przewidzenia, skoro nie wybralimy 1adnego pliku. MediaPlayerl nie otrzymuje
w=aciwej nazwy pliku do otwarcia. Nic wic dziwnego, 1e prba wykonania jego metody
Open() (otwrz plik) wywo=uje b=/d. Nale1a=oby zatem tak zorganizowa@ nasze 3 linie kodu, aby
ostatnie dwie z nich wykonywa=y si tylko wtedy, gdy u1ytkownik rzeczywicie wskaza= jaki
plik. Mo1na to bardzo =atwo osi/gn/@ poprzez wykorzystanie instrukcji warunkowej if (jeli).
9. Modyfikujemy wic nasz/ funkcj nastpuj/co:

void__fastcall TForml::ButtonlClickCTObject *Sender)
{
OpenDialogl->Execute();
if (OpenDialogl->FileName!="") // jeli sk=adowa FileName" nie jest
// pusta (jej zawarto@ jest r1na
// od "") to wykonaj instrukcje midzy
// {" a }"
{
Medi aPlayerl->Fi 1 eName=OpenDi al ogl->Fi 1 eName;
MediaPlayerl->Open();
}
}
10. Pozostaje nam jeszcze dokonanie kosmetycznych zmian rozmiarw formularza, napisu na
przycisku itp. Ostatecznie nasza aplikacja mo1e wygl/da@ na przyk=ad tak:
Podsumowanie: czy to ju1 wszystko? Oczywicie, 1e nie! Sprbujcie rozda@ ten program
znajomym. Za=o1 si, 1e natkn/ si na dziesi/tki r1nego typu niedoci/gni@. Nie mo1emy
wszystkiego przewidzie@. Nale1y jednak uwzgldnia@ sytuacje najbardziej prawdopodobne -
wtedy nasza aplikacja ma szans na bezproblemowe dzia=anie.

2. Biblioteka VCL - przegl/d najwa1niejszych komponentw
Wprowadzenie: w poni1szych @wiczeniach zaznajomimy si skrtowo z tym, co stanowi o
sile pakietu C++ Builder - komponentami wizualnymi.
VCL to skrt od Visual Component Library (biblioteka komponentw wizualnych).
Sk=ada si ona z gotowych obiektw. Dziki nim projektowanie interfejsu u1ytkownika jest
du1o prostsze. Mamy rwnie1 =atwy dostp do najbardziej nawet skomplikowanych
mechanizmw Windows.
W(asno.ci komponentw
Wygl/d, rozmiary, styl i wiele innych w=aciwoci obiektu mo1emy zmienia@ za
pomoc/ Inspektora Obiektw. Sk=ada si on z dwch czci: karty w=aciwoci (Properties) i
karty zdarzeA (Events).
W kodzie programu do w=aciwoci odwo=ujemy si za pomoc/ nazwy obiektu i
oddzielonej strza=k/" (operatorem wskazania) nazwy w=aciwoci tego obiektu, na przyk=ad:
OpenDia"logl->FilenName;
Na w=asnociach mo1emy dokonywa@ operacji zapisu (podstawienia) i odczytu, lub
tylko odczytu w zale1noci od rodzaju elementu.
Metody komponentw
Ka1dy z komponentw ma zestaw swoich wewntrznych metod, czyli mo1liwych do
wykonania na danym obiekcie operacji. I tak na przyk=ad wewntrzna metoda komponentu
OpenDialogl - Execute() otwiera=a okienko dialogowe i czeka=a na potwierdzony wybr lub
anulowanie operacji:
OpenDialogl->Execute();
Zdarzenia zwiEzane z komponentem
Ostatni/ z trzech g=wnych cech zwi/zanych z ka1dym komponentem jest jego
mo1liwo@ reakcji na r1ne zdarzenia. Zdarzenia s/ powi/zane z dostpnymi do edycji
funkcjami, w ktrych zakodowa@ nale1y reakcje na nie. Gdybymy chcieli na przyk=ad, aby
po klikniciu w przycisk Buton 1otwiera=o si okienko dialogowe Dialo musielibymy
dokona@ edycji funkcji obs=uguj/cej zdarzenie MouseClick przycisku Buttonl (Inspektor
Obiektw - karta Events pozycja OnMouseClick) i zapisa@ tam kod, ktry ma si wykona@ w
razie zajcia zdarzenia:

OpenDialogl->Execute();
Komponent zawiera zwykle zestaw standardowo obs=ugiwanych zdarzeA, takich jak
kliknicie mysz/ (na danym komponencie), przesunicie myszy nad komponentem,
nacinicie klawisza itp.
Pomoc on line" Borlanda
Stanowczo zachcam do studiowania pomocy on lin"! Nawet osoby, ktre nie znaj/
(lub wydaje im si, 1e nie znaj/) jzyka angielskiego, wynios/ z jej przegl/dnicia wa1ne
dowiadczenia. Wystarczy zaznaczy@ odpowiednio obiekt, nacisn/@ FI i ju1 mamy na ekranie
jego dokumentacj! Mimo braku znajomoci jzyka angielskiego, nie wolno nam si zra1a@.
Samo przegl/dnicie zestawu w=aciwoci (properties) i metod (methods) zwi/zanych z
danym komponentem mo1e podsun/@ mas nowych pomys=w!
Karta Standard"
Rysunek 2.1. Komponenty karty Standard
MainMenu - projektowanie menu zwi/zanego z formularzem. Niewidoczne. Wystarczy
przeci/gn/@ na formularz, klikn/@ dwukrotnie i pos=uguj/c si klawiszem insert" oraz
prawym przyciskiem myszy zaprojektowa@ je wed=ug w=asnego uznania.
PopupMenu - to menu podrczne, ktre pojawia si po naciniciu prawego przycisku myszy
nad zwi/zanym z nim komponentem (w naszym przyk=adzie - Label"). Edycja - podobnie
jak dla poprzedniego komponentu.
Label - statyczny tekst na formularzu.
Edit - pozwala na edycj jednego wiersza tekstu.
Memo i Button - edycja wielu wierszy tekstu oraz przycisk.
CheckBox - pole wyboru; dwa mo1liwe stany - w=/czony, wy=/czony.
ListBox - lista elementw, ktre mo1na zaznacza@ lub wybiera@.
ComboBox - komponent podobny do poprzedniego. Posiada dodatkowo mo1liwo@
wprowadzenia tekstu.
ScrollBar - pasek przewijania.
GroupBox, RadioGroup i Panel - obiekty grupuj/ce inne obiekty.

Karta Additional"
Rysunek 2.2. Karta Additional
BitBtn - przycisk rozszerzony o mo1liwo@ zamieszczenia rysunku (w=asno@ Glyph).
SpeedButton - przycisk u1ywany w paskach narzdziowych.
MaskEdit - formatowane pole edycji. Wiele predefiniowanych formatw numerycznych i
znakowych.
StringGrid - arkusz z elementami bd/cymi =aAcuchami znakw.
DrawGrid - podobnie jak wy1ej, lecz dla grafiki.
Image - wywietlenie pliku graficznego (bitmapa, ikona lub metaplik).
Shape -jedna z kilku prostych figur geometrycznych.
Bevel - pomocny przy uzyskiwaniu efektu trjwymiarowoci (wybrzuszony lub wkls=y
prostok/t).ScrollBox - przewijane okienko.
Karta ,,Win95'
Rysunek 2.3. Karta Win95
TabControI - tworzenie zak=adek.
PageControl - zak=adki powi/zane z kartami.
TreeView - element u=atwiaj/cy wywietlanie drzewiastych" struktur.
ListView - wywietlanie etykiet wraz z ikonami.
ImageList - obiekt bd/cy list/ elementw graficznych.
Header - nag=wek.
RichEdit - pole dla tekstu w formacie (*.rtf).
StatusBar - linia stanu.
TrackBar - suwak.
ProgresBar - wskaEnik postpu.
UpDown - zmniejszanie lub zwikszanie wartoci.
HotKey - tworzenie gor/cych" klawiszy.

Karta Data Access"
Rysunek 2.4. Karta Data Access
Na karcie tej znajduj/ si komponenty organizuj/ce wsp=prac z bazami danych.
DataSource, Table - Erd=o danych i reprezentacja tabeli danych.
Query - komunikacja z baz/ danych oparta na jzyku SQL.
StoredProc - wywo=ywanie procedur zapamitanych na serwerze bazy danych.
Database - komunikacja z serwerem danych.
Sesion - globalne zarz/dzanie po=/czeniami z bazami danych realizowanymi przez aplikacj.
BatchMove - operacje na grupach rekordw lub ca=ych bazach danych.
UpdateSQL - aktualizacja danych.
Karta Data Controls"
Rysunek 2.5. Karta Data Controls
Komponenty s=u1/ce do edycji baz danych.
DBGrid - arkusz edycji tabel.
DBNavigator - organizuje poruszanie si w tabeli danych.
DBText - komponent wywietla zawarto@ pola danych, z ktrym jest zwi/zany.
DBEdit - jak wy1ej plus mo1liwo@ edycji pola.
DBMemo - jak wy1ej - edycja w wielu wierszach.
DBImage - obiekt zwi/zany z polem graficznym tabeli danych (wywietlenie rysunku
zawartego w bazie danych).
DBListBox - zwi/zane z tabelami pole listy.
DBComboBox - obiekt ,,ComboBox" zwi/zany z bazami danych.
DBCheckBox - obiekt ,,CheckBox" zwi/zany z bazami danych.
DBRadioGroup - obiekt ComboBox" zmodyfikowany do wsp=pracy z bazami danych.
DBLookupListBox - obiekt ListBox" zmodyfikowany do wsp=pracy z bazami danych.

DBLookupComboBox - obiekt ComboBox" zmodyfikowany do wsp=pracy z bazami
danych. Tabela szczeg=owa.
Karta Win 3.1"
Rysunek 2.6. Karta Win31
Obiekty na tej karcie maj/ zwykle swe odpowiedniki na innych kartach. Pozostawiono
je, aby utrzyma@ zgodno@ z wczeniejszymi (16- bitowymi) wersjami projektw.
DBLookupList - obiekt ListBox" zmodyfikowany do wsp=pracy z bazami danych. Tabela
szczeg=owa.
DBLookupCombo - obiekt ComboBox" zmodyfikowany do wsp=pracy z bazami danych.
Tabela szczeg=owa.
TabSet - zak=adki.
Outline - wywietlanie struktur drzewiastych".
Header - nag=wek sk=adaj/cy si z wielu sekcji.
TabbedNoteBook - zak=adki plus karty.
NoteBook - zestaw kart.
wiczenie 2.1. Komponenty z karty Standard"
Zadanie 2.1.1: za pomoc/ komponentu MainMenu wyposa1y@ aplikacj w menu
umo1liwiaj/ce zmian koloru formularza.
Wykonanie:
1. Otwieramy nowy projekt i z karty Standard pobieramy komponent MainMenu.
2. Aby dokona@ modyfikacji menu, musimy klikn/@ podwjnie w komponent Ma-inMenul
na naszym formularzu lub wybra@ jego w=asno@ Items.
3. Na ekranie pojawi si projekt pustego jeszcze menu (Forml->MainMenu). Teraz
wystarczy wpisa@ nazw pierwszej opcji naszego menu - Kolor" (w Inspektorze
Obiektw zauwa1ymy zmian w=asnoci Caption).
4. Pos=uguj/c si klawiszami kursora, Enter, Insert i Delete mo1emy dowolnie mo-
dyfikowa@ wygl/d menu. Dodajmy do opcji Kolor trzy podpozycje - ,Bia7y", Niebieski"
i Czerwony". Teraz jeszcze obok do=1my opcj Pomoc".
5. Mo1emy przyst/pi@ do przypisywania operacji do naszego menu. Ustawmy si na pozycji
Bia(y. Po dwukrotnym klikniciu w pozycj menu (mo1na te1 wybra@ z karty Events
Inspektora Obiektw zdarzenie OnCIick), pojawi si puste cia=ofunkcji. Odpowiada@ ona
bdzie na wybr pozycji Bia=y, musi zatem odpowiednio zmienia@ kolor formularza (j
e
8
w=asno@ Color). Wpisujemy wic ,Forml->Color=clWhite".
6. Podobnie postpujemy z pozycjami Niebieski i Czerwony wpisuj/c odpowiednio ,forml-
>Color=clBlue;" i ,Forml->Color=clRed".
7. Pozosta=a nam do oprogramowania opcja Wiadomo@. Wjej w=asno@ OnClick wpiszemy
wywo=anie okienka z informacj/ o programie:
HessageBox(0."Program zmienia kolor formularza","Wiadomosc".0);
8. Zapisujemy projekt (zad211) i mo1emy uruchomi@ nasze nowe dzie=o.
Podsumowanie: nauczylimy si, jak w prosty sposb doda@ do formularza menu g=wne, a
nastpnie przypisa@ r1ne operacje jego kolejnym pozycjom. Je1eli chcemy w naszej aplikacji
korzysta@ jednoczenie z wielu r1nych opcji - komponent Main-Menu jest niezast/piony.
Zadanie 2.1.2: wykona@ menu podrczne za pomoc/ komponentu PopupMenu.
Wykonanie:
1. Potrzebna nam bdzie etykieta (Label), na ktrej umiecimy napis (Caption) z informacj/
dla u1ytkownika (Wcinij prawy klawisz myszy").
2. Teraz pobieramy z karty Standard dwa obiekty PopupMenu. Modyfikacj menu
podrcznego (PopupMenu) przeprowadza si tak samo, jak w wypadku menu g=wnego
(MainMenu). W PopupMenu 1 dodajemy pozycje ,Niebieski" i Zielony" wpisuj/c w
odpowiednie zdarzenia OnClick ,Labell->Color=clBlue;" i Labell->Color=clGreen;.
3. Jak wida@ zmienialimy kolor etykiety - bdzie to wic podrczne menu etykiety Label 1.
Zagl/dnijmy w jej w=asno@ PopupMenu i wybierzmy z listy PopupMe-nul.
4. Poniewa1 nasza etykieta wygl/da nieco rachitycznie, zmieAmy jej w=asno@ Font -
>Height (Font->Wysoko@) na 25.
5. Teraz zabierzemy si za menu podrczne formularza, ktrym zostanie PopupMenu2.
Dodajemy pozycje ,Biia7y" i ,Szary" wpisuj/c odpowiednio w ich zdarzeniu OnClick
,Forml->Color=ctWhite;" i ,Forml->Color=clGray;".
6. Pozostaje nam podpi/@" menu podrczne PopupMenu2 do formularza (w=asno@
PopupMenu formularza) i po zapisaniu projektu mo1emy uruchamia@ aplikacj.
Podsumowanie: jak na pewno zauwa1ylimy, kliknicie prawym klawiszem myszy na
danym obiekcie powoduje pojawienie si jego menu podrcznego.
Zadanie 2.1.3: przy u1yciu komponentw Edit zaprojektowa@ aplikacj wykonuj/c/ cztery
podstawowe dzia=ania arytmetyczne (+-*/).

Wykonanie:
1. Potrzebne nam bd/ cztery pola edycji (Edit) i sze@ etykiet(Label).
2. Pola edycji ustawiamy obok siebie nadaj/c im kolejno nazwy (w=asno@ Name): JJT,
Op", ,J2
n
i Wn".
3. Nad nimi ustawiamy kolejne etykiety nadaj/c im napisy (Caption): ,,iczbal",
operatori+-'*/)", ,JJczba2" i S".
4. Pozosta=ych etykiet u1yjemy do umieszczenia znaku =" pomidzy wynikiem a drug/
liczb/ oraz umieszczenia tytu=u programu (Operacje arytmetyczne").
5. Teraz strona operacyjna - zaczniemy od kodu g=wnego, czyli wyliczenia zawartoci pola
edycji Wn (wynik) w zale1noci od wartoci pl LI, L2 i Op. Nasze wyliczenie bdzie
odpowiedzi/ na kliknicie w obiekt Wn. Pod w=asno@ OnClick pola edycji Wn
podpinamy wic:
void __f ast cal l TForml::WnClick(T0bject *Sender)
{
// pod zmienne SI i S2 pobieramy rozmiar tekstw w polach edycji LI i L2
int S1 - Ll->GetTextLen();
int S2 - L2->GetTextLen();
char buf[10]; // 10-cio znakowy bufor pomocniczy
// tworzymy $a(cuchy znakowe C1.C2.C3 na Liczbe.1, Liczb2i operator
char *C1 - new char[++Sl];
char *C2 - new char[++S2];
char *C3 - new char[2];
// do $a(cuchw znakowych Cl i C2 pobieramy tekst w polach LI i L2
// o d$ugoci SI i S2
Ll->GetTextBuf(Cl. SD;
L2->GetTextBuf(C2, S2);
Op->GetTextBuf(C3.2); // interesuje nas tylko pierwszy znak w polu Op
// w zale1noci od pierwszego znaku $a(cucha C3 ([0])
// wykonywane bd2 r1ne warianty (case '+', case '-' itd.)
switch (C3C03)
{
case '+': // dodawanie
itoa(atoi(Cl)+atoi(C2),buf.10);// konwersja suma Cl i C2 - tekst
break;
case '-': //odejmowanie
itoa(atoi(Cl)-atoi(C2),buf.10);//konwersja C1-C2 - tekst
break;
case '*': //mno1enie
itoa(atoi(Cl)*atoi(C2).buf.l0);// C1*C2 - tekst (buf)
break;
case '/': //dzielenie
itoa(atoi(Cl)/atoi(C2).buf,10);// C1/C2 - tekst
break;
default: // domylnie - dodawanie
itoa(atoi (CD+atoi (C2) .buf .10);
}
// niezale1nie od wariantu - na koniec ustawiamy tekst w polu edycji Wn
// na nasz bufor pomocniczy buf, czyli wynik operacji
Wn->SetTextBuf(buf); }
6. Program jest ju1 gotw. Mo1emy nawet uruchomi@ go w celu obejrzenia efektw.
7. Nasza aplikacja jest jeszcze zbyt niewygodna w dzia=aniu - u1ytkownik musi klikn/@ w
wynik, aby uzyska@ jakiekolwiek efekty. Jak zrobi@, aby wynik sam liczy= si we
w=aciwym momencie? Musimy bada@ stan pl edycji L1, L2 i Op i w razie jakiej
kolwiek ich zmiany oblicza@ wynik za pomoc/ funkcji WnClick (powy1ej). Wykonanie
tego jest wbrew pozorom bardzo proste - wystarczy wykorzysta@ zdarzenie OnChange
(zmiana) pl L1 L2 i Op i umieci@ tam wywo=anie funkcji WnClick (WnClick(this)").
8. Zauwa1ylimy ju1 na pewno, 1e ka1da metoda (funkcja) bd/ca odpowiedzi/ na
zdarzenie (np. ta w punkcie 5) wymaga parametru, ktrym jest wskaEnik do obiektu
wywo=uj/cego t funkcj (TObject *Sender). Wywo=uj/c j/ wic rcznie" w punkcie 7
(bo przecie1 nie nast/pi=o kliknicie w pole Wn) musimy za=/czy@ wskaEnik do jakiego
obiektu. Najlepiej jest nie oszukiwa@ i zgodnie z prawd/ poda@ wskaEnik do aktualnego
obiektu, czyli this. Wten sam sposb postpujemy dla wszystkich pl, na ktrych zmiany
chcemy reagowa@ (L1,L2 i Op)
9. Zapisujemy projekt i uruchamiamy go dla sprawdzenia efektw. Podsumowanie:
nauczylimy si wykorzystywa@ pola edycji (Edit), a tak1e reagowa@ na ich zmiany
(OnChange). Na podkrelenie zas=uguje fakt, 1e metody (takie jak WnClick) bd/ce
odpowiedziami na zdarzenia, niczym nie r1ni/ si od innych metod obiektu (mo1e poza
ich specjalnymi parametrami). Mo1emy wic je wywo=ywa@ z kodu naszego programu
niezale1nie od tego, czy zdarzenie, ktre obs=uguj/ zasz=o, czy nie!
Zadanie 2.1.4: zbudowa@ program operuj/cy na schowku z wykorzystaniem komponentu
Memo.
Wykonanie:
1. Potrzebne komponenty to dwa przyciski (Button) i komponent Memo.
2. Za pomoc/ jednego z przyciskw bdziemy kopiowa@ zawarto@ Memol do schowka.
Ustawiamy jego w=asno@ Caption na ,JCopiuf\ a w=asnoci OnClick
przyporz/dkowujemy funkcj:
void__fastcall TForml::ButtonlClick(TObject *Sender)
{
// wykorzystujemy metod CopyToClipboard, aby skopiowa3
// zawarto3 Memol do schowka Memol->CopyToClipboard();
}
3. Teraz drugiemu przyciskowi nadajemy napis (Caption) Wklej i w=asno@ OnClick:
void _ f ast cal l TForml::Button2Click(TObject *Sender)
{
// wklejenie ze schowka - metoda PasteFromClipboard
Memol->PasteFromCli pboardC);
}
4. Dla niezdecydowanych u1ytkownikw mo1emy teraz jeszcze przypisa@ przyci-
skom.w=asno@ Hint (podpowiedz) odpowiednio ,JKopiowanie do schowka zaznaczonego
tekstu" i Wklejenie zawartoFci schowka". Ustawiamy jeszcze w=asnoci ShowHint
(poka1 podpowiedz) obu przyciskw na true.
Podsumowanie: nauczylimy si korzysta@ z komponentu Memo. Jego charakterystyczn/
cech/ jest mo1liwo@ zapamitania wielu linii tekstu we w=asnoci Lines klasy TStrings.
Dowiedzielimy si rwnie1, w jaki sposb korzysta@ ze schowka w naszych programach. Na
uwag zas=uguje mo1liwo@ wpisania tekstu we w=aciwo@ Hint. Po ustawieniu innej
w=aciwoci - ShowHint na tnie, nakierowanie wskaEnika myszy na przycisk spowoduje
pojawienie si podpowiedzi.
Jeli chodzi o TMemo - nie ma miejsca w tej ksi/1ce, aby dok=adnie opisa@ wszystkie
w=asnoci i metody tego i innych komponentw. Jeszcze raz zachcam wic do ekspe-
rymentw. Nie bjcie si prbowa@ w ciemno" - cho@bycie stawali na g=owach, dymy z
komputerw nie pjd/! Przy u1yciu standardowej pomocy w C++ Builderze mo1ecie zdzia=a@
bardzo wiele! Nawet nie znaj/c angielskiego mo1na przecie1 wywietli@ list w=asnoci i
metod danego obiektu, a potem eksperymentalnie ich u1ywa@, orientuj/c si co do ich typu i
typu ich parametrw. Jeli na przyk=ad chcemy przekona@ si, co wykona metoda Clear
obiektu klasy TMemo, wystarczy zajrze@ w plik pomocy i obejrze@ jej definicj:
void _ f ast cal l Cl ear ( voi d) ;
Nie musimy zna@ angielskiego, aby si przekona@, 1e jest to metoda, ktra nie wymaga
1adnych parametrw { (void) }, ani nie zwraca 1adnych wartoci |void przed nazw/ metody}.
Jej u1ycie bdzie wic bardzo proste - ,Memol->Clear{);". A co z jej dzia=aniem? O tym
przekonamy si w praktyce! Dodajemy nastpny przycisk do powy1szego projektu, w jego
w=asno@ OnCIick podpinamy wywo=anie ,JMemol ->Clear();" i - niech si co chce dzieje -
uruchamiamy aplikacj. Dzia=a? Wiecie ju1 jak? O to w=anie chodzi. Wszystkiego nie
dowiecie si z ksi/1ek. Najbardziej wartociowe dowiadczenia wyniesiecie z
eksperymentw! Pamitajcie o tym brn/c przez dalsze zadania. Starajcie si zawsze
pogrzeba@" troch przy ka1dym zadaniu, uzupe=ni@ je o to, co Was interesuje, czy te1
zwrci=o Wasz/ uwag.
Zadanie 2.1.5: przy u1yciu obiektw RadioButton skonstruowa@ program zmieniaj/cy kolor
formularza.
Wykonanie:
1. Bd/ nam potrzebne trzy obiekty RadioButton i jedna etykieta, za pomoc/ ktrej
poinformujemy u1ytkownika, co te1 takiego wykonuje program.
2. Etykiecie nadajemy napis (Caption) Wybierz kolor t7a formularza", a kolejnym RadioButton
Zielony, H7ty", Czerwony"'.
3. Teraz w=asnociom OnClick kolejnych RadioButton przypisujemy funkcje zmieniaj/ce kolor
formularza:

void __fastcall TForml::RadioButtonlClick(TObject *Sender)
{
// u1ytkownik klikn/= w "Zielony"
if (RadioButtonl->Checked)// jeli zaznaczony
Forml->Color=clGreen; // zmieniamy kolor Forml na zielony
}
void _fastcall TForml::RadioButton2Click(TObject *Sender)
{
if (RadioButton2->Checked)
Forml->Color=clYelIow; // 1=ty
}
void__fastcall TForml::RadioButton3Click(T0bject *Sender)
{
if (RadioButton3->Checked)
Forml->Color-clRed; // czerwony
}
Podsumowanie: obiektami RadioButton najlepiej pos=u1y@ si, gdy chcemy u1ytkownikowi
udostpni@ kilka okrelonych wariantw jakiej operacji czy danych. Bardziej oglne
zastosowanie zyskuje tu komponent RadioGroup, ktry wspaniale nadaje si do
eksperymentw.

Zadanie 2,1.6: komponent LislBox.
Wykonanie:
1. Pobieramy pole listy (ListBox).
2. Zaczniemy od pola edycji (Edit), za pomoc/ ktrego bdziemy wpisywa@ kolejne
elementy do ListBoxl. Nad nim umieszczamy etykiet z napisem Tu wpisz kolejny
element listy".
3. Teraz jeszcze przycisk z napisem ,Dodaj element". W jego w=asno@ OnClick wpiszmy
funkcj dodaj/c/ nowy element do ListBoxl:

void__fastcall TForml;:ButtonlClicktTObject *Sender)
{
// dodajemy tekst z Editl do istBoxl
ListBoxl->Items->Add<Editl->Text);
}
4. Aby urozmaici@ nasz program, mo1emy pod w=asno@ OnClick ListBoxl przypisa@ jeszcze jedn/
funkcj:
void__fastcall TForml::ListBoxlClicktTObject *Sender)
{
//ustawiamy zawarto@ pola edycji na aktywny element pola listy
Editl->Text=l.istBoxl->Items->Strings[ListBox1->ItemIndex];
}
5. Pozostaje nam zapis projektu, kompilacja i ocena dzia=ania.
Podsumowanie: obiekt ListBox jest przydatny, gdy chcemy udostpni@ zmieniaj/ca si list
opcji u1ytkownikowi. Jeli chodzi o nasz projekt, mo1na doda@ kolejny przycisk do usuwania
elementw i pos=u1y@ si metod/ Delete.
Zadanie 2.1.7: komponent ScrollBar,
Wykonanie:
1. Bdzie nam oczywicie potrzebny ScrollBar, oraz przycisk z jakim zachcaj/cym
napisem, na przyk=ad PrzesuK mnie".
2. ScroIlBarl umieszczamy u do=u formularza, a jego w=asnoci OnChange przypo-
rz/dkowujemy funkcj:
void __fastcall TForml::ScrollBarlChangelTObject *Sender)
{
int Margines=10; // zak)adamy margines
int PKrawedz,LKrawedz;//sk0d dok0d ma si1 przesuwa4 przycisk
PKrawedz-Foniil->Width-Buttonl->Width-Margines; // wyliczamy prawe
// skrajne po)o8enie
LKrawedz=Margines: // lewe skrajne po)o8enie
int Zakres-PKrawedz-LKrawedz; // zakres
// ustawiamy po)o8enie przycisku w zale8no:ci od po)o8enia ScroIlBarl
Button1>Left=ScrollBarl->Position*(Zakres)/(ScrollBarl->Max)+LKrawedz;
}
Podsumowaniu: 1artobliwie spraw okreliwszy: obiekt ScrollBar mo1emy u1y@ do
przesuwania wszystkich i wszystkiego. Jeli jest nam potrzebne przesunicie w odniesieniu
do konkretnego elementu u1yjmy ScrollBar, je1eli tych elementw jest wicej z
pewnoci/ lepszy bdzie ScrollBox (omwiony dalej).
wiczenie 2.2. Komponenty rozszerzone (karta Additional")
Zadanie 2.2.1: skonstruowa@ program demonstruj/cy mo1liwoci komponentu Mask-Edit.
Wykonanie:
1. Pobieramy etykiet i wpisujemy w niej (Caption) Przyk7ady uLycia w7asnoFci EditMask
obiektu MaskEdit". Aby by=a widoczna, ustawiamy Font->IIeight (czcionka
wysoko@) na 20.
2. Teraz pobieramy kolejne etykiety i komponenty MaskEdit ustawiaj/c kolejno ich
w=asnoci Caption i EditMask:

Caption etykiety EditMask obiektu MaskEdiI
..Tu mo1esz wpisa3 tylko do 10-ciu cyfr 9999999999 :1;_"
od 1 do 9",
S$owo zaczynaj2ca sie z du1ej litery ..>L<>111 11111111 ; 1 ; "
(np. nazwisko)",
,.6-cyfrowy nr tel. wraz z 3-cyfrowym numerem ! \ ( \099\ )00-00-00; 1 ;__"
kierunkowym",
3. Zapisujemy projekt i uruchamiamy aplikacj.
Podsumowanie: komponent MaskEdit jest u1ywany do zaw1enia zakresu danych, ktre
mog/ hyc wpisywane do pola edycji. W=asno@ EditMask nie dopuszcza do wprowadzenia
przez u1ytkownika nieprawid=owych (niezgodnych z mask/) znakw
Jeli u1ytkownik sprbuje wpisa@ nieprawid=owy znak, maskowane pole edycji nic przyjmie
go. Do odrzucenia lub przyjcia ca=ej wprowadzonej wartoci mo1na u1y@ zdarzenia
OnValidate.
Maska (EditMask) sk=ada si 7 trzech czci rozdzielonych rednikami. Pierwsza to sama
maska. Druga jest znakiem okrelaj/cym, czy znaki literalne samej maski zostan/ zapisane
jako cz@ danych. Cz@ trzecia to znak u1ywany dla reprezentacji nic wype=nionego jeszcze
miejsca.
Poni1ej podane s/ znaki specjalne u1ywane do utworzenia pierwszej czci maski.
Znak Znaczenie
! Jeli ten znak pojawi si w masce, wiod/ce puste miejsca nie zostan/
uwzgldnione. Jeli nie - koAcowe ptisie miejsca nie zostan/ uwzgldnione.
> Wszystkie nastpne znaki bd/ pisane du1ymi literami a1 do koAca maski
lub do znaku "<".
< Wszystkie nastpne znaki bd/ pisane ma=ymi literami a1 do koAca maski
lub do znaku ">".
<> Od tego miejsca nic bdzie sprawdzana wielko@ liter.
\ Znak nastpny po tym znaku nie bdzie interpretowany (mo1na to wyko-
rzysta@ do wstawienia znaku specjalnego bezporednio w dane).
L Wmiejscu tego znaku przyjmowane bd/ tylko litery alfabetu (A-Z, a-z).
l Zezwala tylko na znaki jw., ale nie wymaga ich.
A Tylko znaki alfanumeryczne (A-Z, a-z, 0-9).
a Zezwala na znaki jw. ale ich nie wymaga
C Wymagany dowolny znak w tej pozycji,
c Zezwala na znak jw., nie wymagaj/c go.
0 Tylko znak numeryczny.

Znak Znaczenie
9 Zezwala na znak jw.
# Zezwala na znak numeryczny lub +/-", ale nie wymaga ich.
: U1ywany do rozdzielania godzin, minut i sekund. Znak, ktry pojawi si
w danych zale1y od ustawieA W95.
/ U1ywany do rozdzielenia dni, miesicy i lat w datach.
; U1ywany do rozdzielenia kolejnych czci maski.
Automatycznie wstawia puste miejsce. Przy w prowadzaniu danych kursor ominie puste pole.
Wdrugiej czci maski mo1emy wpisa@:
0 - znaki maski zostan/ pominite w danych;
kaTdy inny znak - znaki literalne maski zostan/ w danych uwzgldnione.
Wczci trzeciej wpisujemy zwykle znak podkrelenia(_") lub spacj.
Zadanie 2.2.2: utworzy@ przegl/dark do plikw BMP dowolnych rozmiarw za pomoc/
komponentw ScroIlBox i Image.
Wykonanie:
1. Prcz powy1szych komponentw (karta Additional) bdziemy potrzebowa@ rwnie1
okienka Open Dialog (karta Dialogs) do wybierania plikw BMP (ustawiamy w=asno@
Filter na Pliki BMP" i *.bmp").
2. Komponent Image 1 pobierzmy tak, aby znalaz= si wewn/trz obiektu ScrwllBoxl.
3. Ustawiamy w=asno@ AutoSize (automatyczny rozmiar) obiektu Imagel na true.
Nastpnie pobieramy przycisk nadaj/c mu napis Otwrz obrazek". Jego zdarzenie
OnClick zapisujemy:
void__fastcall TForml::ButtonlClick{TObject *Sender)
{
If (OpenDialogl->Execitte()) // jeli potwierdzenie
Imagel->Picture->LoadFromFile{OpenDialogl->FileName>;
}
4. Wystarczy teraz uruchomi@ aplikacj i otworzy@ jaki wikszy obrazek, aby przekona@
si, jak wygl/da ScroIlBox w dzia=aniu (rysunek 2.7).
Podsumowanie: obiekt ScrolIBox bdzie dla nas pomocny w sytuacjach, gdy mamy zbyt
ma=o miejsca na formularzu do przedstawienia wszystkich danych jednoczenie. Pozwala on
na stworzenie wirtualnej" powierzchni, po ktrej mamy mo1liwo@ przesuwa@ okienko.
Warto przyjrze@ si bli1ej takim jego w=asnociom, jak AutoScroll, HorzScruIIBar,
YertScrllBar.

Rysunek 2.7. ScrollBox w dzia=aniu
wiczenie 2.3. Okna dialogowe (karta Dialogs")
Wprowadzenie: umiemy ju1 korzysta@ z okienka dialogowego OpenDialog. C++ Builder ma
w swoim arsenale wiele innych standardowych okienek dialogowych. Dwa z nich
przedstawiamy w poni1szym zadaniu.
Zadanie 2.3.1: utworzy@ okienka dialogowe FontDialog i ColorDialog.
Wykonanie:
1. Potrzebne nam bd/ dwa przyciski z napisami Wybierz kolor" i Wybierz czcionk".
2. Dodajemy do formularza ColorDialog i FontDialog z karty Dialogs.
3. W zdarzeniu OnClick pierwszego przycisku wpisujemy:
void__fastcall TForml::ButtonlClick(TObject *Sender)
{
// korzystamy z tego, 1e metoda Execute() zwraca true. jeli
// u1ytkownik zamknie okno dialogowe z potwierdzeniem
// (jest to lepszy sposb ni1 w 3wiczeniu z odtwarzaczem)
if (ColorDialogl->Execute()) // jeli potwierdzono
Forml ->Color-ColorDialogl->Color;
}
4. W=asno@ OnClick drugiego przycisku pod=/czymy pod funkcj:
void _fastcall TForml::Button2Click(TObject *Sender)
{
if (FontDialogl->Execute())
Fonyil->Font=FontDialogl->Font:
}
5.Gotowe! Po zapisaniu projektu (zad231) mo1emy uruchamia@ program.
Rysunek 2.8. Okno dialogowe ColorDialog
wiczenie 2.4. Obiekty z karty Win95"
Zadanie: 2.4.1: utworzy@ komponenty PageControl, StatusBar, ProgresBar.
Wykonanie:
1. Zaczniemy od komponentu PageControl. Pobierzmy go z karty Win95. Poniewa1 w
naszym przyk=adzie bdzie on mia= dwie strony, dodajmy jeszcze jedna, naciskaj/c prawy
klawisz myszy i wybieraj/c Add Page. Na pierwszej z nich umiecimy etykiet To jest
pierwsza strona", a na drugiej - To jest strona druga".
2. Dodajemy StatusBar (karta YVin95) modyfikuj/c jego w=asno@ Panels (wybieramy
New i wpisujemy w pozycji Text ,Aktywna strona" naciskaj/c Enter).
3. W zdarzeniu OnChange (zmiana) obieklu PageControll wpisujemy:
void __fastcall TForml::PageControlIChangefTObject *Sender)
{
// ustawiamy tekst pierwszej rubryki StatusBarl na nazw aktywnej
// strony w PageControll
Status Barl->Panel s->ItemsLO]->Text="Aktywna strona: "+PageControl1-
>ActivePaqe->Caption;
}
4. Bd/c na stronic pierwszej pobieramy etykiet, ktra pos=u1y nam za licznik. Ustawiamy
jej napis (Caption) nu .,1". w=asno@ AutoSize ustawiamy na false i poszerzamy. Aby nie
zostawa=y na ekranie jej stare wartoci, ustawiamy w=asno@ Aligmet na inRiglitJitstify"
(dosunicie do prawej). Mo1na tak1e ustawi@ Font->Height na 20.
5. Nastpny element naszej uk=adanki to MaskEdit. U1yjemy go do wskazania koAcowej
wartoci, do ktrej bdzie zwiksza= si licznik. Interesuj/ nas zatem tylko liczby - we
w=asno@ EditMask wpisujemy. 9999999999". We w=asnoci Text pozostawiamy:
100". Obok umie@my etykiet ,Licz do:".
6. Przejdziemy teraz do komponentu ProgressBar. Pobieramy go z karty Win95 ustawiaj/c
jego w=asno@ Max na 100.
7. Jeszcze przycisk, ktry zapocz/tkuje liczenie (Caption - ,Licz"). We w=asno@ OnClick
wk=adamy:
void _fastcall TForml: :ButtonlCl ick(T0bject *Sender)
{
char BuforLlO]; // jak zwykle bufor na tekst
int i; // nasz w$aciwy licznik
// ptla i=od; i <=do; i++
// wykona si (do-od+1) razy
for (i=ProgressBarl->Min;i<=ProgressBarl->Max; i++)
{
itoa(i.Bufor,10); // konwersja liczba - tekst
AnsiString Licznik(Bufor); // w zmiennej Licznik otrzymujemy tekst
// we w$aciwym formacie
Label4->Caption=Licznik; // ustawiamy etykiet na tekstow2'
// reprezentacj licznika
ProgressBarl->Position=i; // aktualizujemy pozycj w ProgressBar1
Label4->Repaint(); // odwie1amy Label4 (inaczej u1ytkownik
// nic nie zobaczy)
) }
8. Jeszcze jeden szczeg=, o ktrym nie wolno nam zapomnie@ - na zmian Mask-Editl
(p. 5) musimy zareagowa@ zmiana, zakresu ProgressBar1:
void __fastcall TForml::MaskEditlChange(TObject *Sender)
{
char BuforCIO]; // bufor na liczb
MaskEditl->GetTextBuf(Bufor.10); // pobieramy 10 znakw z MAskEdit1
// do bufora
ProgressBarl->Max-atoi(Bufor); // nowa maksymalna warto3 dla
// ProgressBar1
}
Podsumowanie: wyjanienia wymaga konstrukcja for({l};{2};{3}). Instrukcja {1} jest
wykonywana przy wejciu do ptli, {2} jest warunkiem powtrzenia ptli, a (3) to instrukcja
wykonywana przy powtarzaniu ptli. Nasze for(i=PrBar->Min; i<PrBar ->Max ;i++) { )
oznacza wic, 1e przy wejciu do ptli pod zmienn/ i zostanie wstawiona warto@ z PrBar-
>Min (i=PrBar->Min;), a nastpnie bdzie ona zwikszana przy ka1dym wykonaniu
instrukcji midzy klamrami { }" (i++;) a1 i osi/gnie warto@ PrBar->Max (i<=PrBar-
>Max).
Warto odr1ni@ ca=y obiekt PageControl od jego kart (TabSheet). Kliknicie (w fazie
projektowania) w obszar zak=adek aktywizuje obiekt PageControl, natomiast kliknicie w
obszar karty (TabSheet) aktywizuje t kart (PageControl->ActivePage). Gdybymy chcieli
zmieni@ nag=wek konkretnej karty, musielibymy klikn/@ w jej obszar, a dopiero potem
szuka@ w Inspektorze Obiektw jej w=asnoci Caption.
wiczenie 2.5. Obiekty z karty System"
Zadanie 2.5.1: przy u1yciu komponentu Timer sporz/dzi@ stoper.
Wykonanie:
1. Z karty System pobieramy obiekt Timer. Z karty Standard pobieramy etykiet, ktra bdzie
naszym licznikiem. Podobnie jak w poprzednim zadaniu musimy wic ustawi@ odpowiednio jej
w=asnoci. Ustawiamy AutoSize na false, Aligment na taRightJustify", Caption na 0".
2. Obiekt Timer posiada tylko jedno zdarzenie - OnTime, czyli tyknicie zegara". Przypisujemy
mu:
void __fastcall TForml::T1merlTimer(T0bject *Sender)
{
// kolejne "tyknicie" zegara - czas zwikszy3 nasz licznik.
char Bufor[6]; // bufor tekstowy na licznik
int 1; // zmienna pomocnicza
Labell->GetTextBuf(Bufor,6); // pobieramy zawarto3 etykiety do Bufor
i=atoi(Bufor); // kowersja tekst- liczba
i++; // zwikszamy "i" o jeden
itoa(1.Bufor,10); //kowersja liczba- tekst (10 oznacza system
//dziesitny)
AnsiString Licz(Bufor); // kowersja na format tekstowy u1ywany przez
// Label->Caption
Labell->Gaption-L1cz; // podstawiamy
Labell->Repaint(): // malowanie etykiety, aby by$o wida3 zmiany
}
3. Z karty Standard pobieramy dwa przyciski nadaj/c im napisy ,Stop/Start" i ,Z.eruj". Ich
zdarzeniom OnCIick przypisujemy:
void _fastcall TForml::Button1Click(T0bject *Sender)
{
if (Timer1->Enabled) //jeli zegar jest "w$2czony"
Timer1->Enabled=false; // "wy$2czamy" zegar
else // w innym wypadku - "w$2czamy"
Timer1->Enabled=true;
}
void __fastcall TForml::Button2ClickCTObject *Sender)
{
// Nast2pi$o kliknicie w przycisk "Zeruj" - u1ytkownik chce wyzerowa3
/ / licznik
Label1->Caption="0"; // zerujemy licznik
}
4. Uzupe=nimy teraz nasz Stoper o mo1liwo@ zmiany interwa=u czasowego pomidzy
kolejnymi tykniciami" zegara Timer1. Odpowiedzialna za to jest jego w=asno@
Interval, w ktrej przechowywana jest liczba milisekund. Pobieramy maskowalne pole
edycji MaskEdit1 z karty Additional, zaopatruj/c je w etykiet interwa7(ms)".
5. Jego w=asno@ EditMask ustawiamy na 99999", a Text na 1000".
6. Teraz musimy jeszcze odpowiednio zareagowa@ na jego zmian...
void __fastcall TForml::MaskEditlChange(TObject *Sender)
{
// Nast2pi$a zmiana interwa$u w polu "edycji - musimy wic zaktualizowa3
// w$asno3 "Interval" komponentu Timerl
char BuforC63; //jak zwykle - bufor na tekst
int i; // zmienna pomocnicza
MaskEditl->GetTextBuf(Bufor,6); // pobieramy do bufora zawarto3
// MaskEditl
i-atoi(Bufor); // konwersja tekst - liczba
Timer1->Interval -i; // ustawiamy nowy i nterwa$
}
7. ... i mo1emy z czystym sumieniem nada@ naszemu formularzowi (Forml) nag=wek
(Caption) Stoper**.
Podsumowanie: po zachcaj/cej rozgrzewce, jak/ zapewne by=o zapoznanie si z obiektami
biblioteki VCL, czas na nieco bardziej zaawansowan/ zabaw. Osadzimy w formularzu
arkusz programu Excel wykorzystuj/c technik OLE (Object Linking & Embedding), a
nastpnie sprbujemy wykona@ na nim par interesuj/cych operacji...
Zadanie 2.5.2: przy u1yciu komponentu OleContainer osadzi@ w formularzu arkusz Excela.
Wykonanie:
1. Potrzebne nam bd/ trzy komponenty - OleContainer (karta System), Button i ListBox.
2. Wybieramy OleContainerl, naciskamy prawy klawisz myszy i wybieramy z menu
Insert Object", a nastpnie z listy Arkusz Microsoft Excei" (mo1na to tak1e zrobi@
dynamicznie za pomoc/ metody CreateObject lub CreateObject-FromFile komponentu
OleContainer).
3. Przyciskowi nadajemy napis pobierz w7asnoF ObjectVerbs komponentu Ole-Containerl".
Zgodnie z tym, co umiecilimy na przycisku, w=asnoci OnClick przypisujemy:
void __fastcall TForml::ButtonlClick(TObject *Sender)
{
// u1ytkownik nacisn2$ przycisk - pobieramy w$asno3 ObjectVerbs
ListBoxl->Items-01eContainerl->0bjectVerbs;
}
4. Zdanie z punktu 1. jest niezbyt prawdziwe - bd/ nam potrzebne jeszcze dwie etykiety
wyjaniaj/ce funkcj ListBoxl: W7asnoF ObjectVerbs komponentu OleContainerl" i
Kliknij, aby wykona odpowiedniS operacjT".
5. W jego zdarzenie OnClick wstawiamy:
void __fastcall TForml::ListBoxlClick(TObject *Sender)
}
// u1ytkownik klikn2$ w ListBox - uruchamiamy wic odpowiedni2
// operacj...
01eContainerl->DoVerb(ListBoxl->ItemIndex);
}
6. Nadesz=a pora na test umiejtnoci naszej nowej aplikacji. Tylko nie wpadajmy w zbytni/
eufori - to nie my napisalimy Excela!
Rysunek 2.9. OLE!
Podsumowanie: i to ju1 wszystko! Niech nie zwiedzie Was pozorna prostota tego zadania. Obiekty
OLE to pot1ne narzdzia z ca=/ mas/ metod i w=asnoci (warto z nimi poeksperymentowa@!). W tym
zadaniu dotknlimy jedynie powierzchni tematu.
Nadszed= czas, aby ods=oni@ nastpne pot1ne narzdzie z bezdennego arsena=u Bor-landa - Debug
Inspektor.
Zadanie 2.5.3: pos=uguj/c si narzdziem Debug Inspector przegl/dn/@ w=aciwoci (Properties),
dane prywatne (Data) i metody (Methods) komponentu OleContainerl
z poprzedniego zadania.
Wykonanie:
1. Otwieramy projekt z zadania 2.5.2 (File\Open Project).
2. Zak=adamy pu=apk"(F5 lub prawy klawisz myszy i Toggle breakpoint) w zdarzeniu OnClick
przycisku (Button1).
3. Uruchamiamy program (F9).
4. Po naciniciu przycisku wykonanie zatrzyma si na naszej pu=apce". PodjedEmy kursorem na
napis (..)OleContainerl(..)" i nacinijmy Alt-F5 (lub prawy klawisz myszy i Inspect).
5. Widzimy okienko bardzo podobne do znanego nam ju1 Inspektora Obiektw. Je1eli jednak
przyjrzymy si bli1ej, zauwa1ymy dyskretne (!) r1nice:
mamy mo1liwo@ przegl/da@ prywatne dane obiektu
mamy mo1liwo@ przegl/da@ wszystkie metody obiektu
informacje dotycz/ nie tylko aktualnego obiektu, ale i jego klas bazowych
mo1emy zagnie1d1a@"' przegl/danie wchodz/c dowolnie g=boko w szczeg=y.
Rysunek 2.10. Debug Inspektor - narzdzie, z ktrego warto korzysta@
Podsumowanie: tak oto zaznajomilimy si (w telegraficznym skrcie) z jednym z
najlepszych narzdzi Borlanda. Przyznam szczerze, 1e nieraz zaoszczdzi=o mi ono wrcz
godzin szperania w dokumentacji. Debug Inspector to pot1ny program. Nauczcie si z
niego korzysta@, a zaoszczdzi Wam to wiele pracy.

3. Programowanie obiektowe w C++ Builderze
Wprowadzenie: w poni1szych @wiczeniach zapoznamy si bli1ej z projektowaniem obiektw w C++
Builderze. Nauczymy si, jak w prosty sposb utworzy@ komponent (obiekt), poznamy obs=ug
zdarzeA i mechanizm dziedziczenia.
C++ definiuje s=owo kluczowe class, ktre pozwala na budowanie obiektw. Zajrzyjmy w plik
nag=wkowy nowego projektu (w edytorze naciskamy Ctrl-F6, cho@ mo1na te1 nacisn/@ prawy
klawisz myszy, a nastpnie wybra@ opcj Open Source/ Header File). Znajdziemy tam kod
definiuj/cy klas formularza:
class TForml: public TForm
{
__published: // identyfikator sekcji __published
// tu mo1na doda@ w=asnoci publikowane
private: // sekcja w=asnoci prywatnych obiektu
// tutaj mo1emy umieci@ w=asnoci widziane" tylko wewn/trz obiektu
public: // publiczne w=asnoci obiektu
//miejsce na w=asnoci publiczne (dostpne z dowolnego innego obiektu)
__fastcall TForm1(TComponent* Owner); // konstruktor obiektu
}
Tak wygl/da definicja klasy, na bazie ktrej mo1emy tworzy@ obiekty. Nie nale1y myli@ tych dwch
poj@ - obiekt i klasa. Klasa jest jedynie opisem (wzorcem) obiektu. Obiekt jest za konkretnym
egzemplarzem klasy. Okrelenie dom" zawiera w sobie takie pojcia jak okno, drzwi, pitra, jest
jednak pewnym wzorcem oglnym. Dopiero gdy okrelimy konkretny jego egzemplarz (np. budynek
przy ul. Jasnej 14"), mamy do czynienia z obiektem.
Przyk=adowa klasa TForml dziedziczy od klasy TForm, czyli bazowej klasy formularzy (do
oznaczenia klasy bazowej s=u1y s=owo kluczowe public - class TForml: public TForm).
Dziedziczenie jest przejmowaniem w=asnoci klas bazowych przez klasy pochodne. Inaczej -
jeli zaprojektowalimy ju1 klas samochd", wraz ze wszystkimi w=asnociami, jak pojemno@
silnika, kolor karoserii itp., to dla zbudowania klasy Mercedes Benz" nie musimy czyni@ tego
wszystkiego od pocz/tku. Wystarczy, 1e jako klas bazow/ w stosunku do Mercedes Benz"
okrelimy klas samochd". Dodajemy tylko jedn/ w=asno@ producent" - i gotowe. Dziedziczenie
to bardzo efektywny mechanizm, za pomoc/ ktrego mo1emy stopniowo budowa@ coraz bardziej
skomplikowane klasy i ich egzemplarze - obiekty.
Definicja naszej klasy TForm1 podzielona jest na kilka sekcji. W sekcji private mamy
mo1liwo@ deklaracji w=asnoci i metod przewidzianych do u1ytku tylko wewn/trz danej klasy. Ten
sposb postpowania mo1na porwna@ do konstruowania telewizora. Wszystkim na pewno wiadomo,
1e telewizor sk=ada si z ca=ej masy specjalistycznych czci. Do jego obs=ugi jednak nie potrzeba nam
1adnej zaawansowanej wiedzy. Dzieje si tak dlatego, 1e wszystkie szczeg=y konstrukcyjne s/ ukryte
(sekcja private) przed u1ytkownikiem. W efekcie wystarczy, 1ebymy wiedzieli, gdzie wcisn/@
w=/cznik. Do udostpnienia takiego w=/cznika" u1ytkownikowi s=u1y sekcja public. Mo1emy tutaj
zdeklarowa@ metody i w=asnoci, ktre bd/ s=u1y=y do sterowania naszym obiektem z zewn/trz.
Sekcji published mo1emy u1y@ do zdeklarowania w=asnoci, ktre bd/ dodatkowo wywietlane
przez Inspektora Obiektw (mo1na te1 elastyczniej operowa@ na danych w niej zawartych - ale o tym
pEniej). Sekcji published mo1emy u1y@ tylko w pochodnych klasy Tob ject. Wszystkie komponenty
z biblioteki VCL s/ pochodnymi Tobject. Funkcj poredni/ midzy private a public pe=ni sekcja
protected. Mo1emy w niej zdeklarowa@ w=asnoci, ktre bd/ widziane tylko przez klasy pochodne w
stosunku do naszej klasy.
Ostatnia pozycja to konstruktor - funkcja o nazwie identycznej z nazw/ klasy (TForm1)
tworz/ca instancj (egzemplarz) obiektu. Mo1emy mie@ rwnie1 do czynienia z destruktorem -
funkcj/ usuwaj/c/ obiekt z pamici. Nazw destruktora tworzymy od nazwy klasy dodaj/c
przedrostek ~".
Jeszcze jedno - obowi/zuje umowa, i1 nazw klasy poprzedzamy du1/ liter/ T (TForm1). Nazwy jej
egzemplarzy (obiektw) s/ tej litery pozbawione (domyln/ nazw/ obiektu klasy TForm1 bdzie
Forml).
wiczenie 3.1. Utworzenie prostej klasy i odpowiadaj*cego jej komponentu (obiektu)
Zadanie 3.1.1: utworzy@ klas o nazwie TLicznik jako pochodn/ klasy TEdit, a nastpnie
zdefiniowa@ w=asno@ prywatn/ FLiczba i metody publiczne ZwiFksz, Zmniejsz i Ustaw.
Wykonanie:
1. Dla uproszczenia pEniejszego procesu kompilacji dodajmy w Menu Options, opcjiEnvironment,
karcie Library, pozycji Include path:
$(BCB)\Projects\Komp;
Uwaga! Powy1szy tekst dopisujemy na koAcu istniej/cych cie1ek! Wszystkie te cie1ki
umo1liwiaj/ dostp do standardowych plikw nag=wkowych. (BCB) oznacza cie1k, pod ktr/
zainstalowany jest C++ Builder. Dla komponentu Licznik wybra=em jej podkatalog
Projects\Komp, ale mg=by to by@ tak1e dowolny inny.
2. Ten sam tekst umieszczamy tak1e na koAcu pozycji Library path i naciskamy OK. W C++
Builderze wybieramy z menu Component opcj New.
3. Pojawi si okienko, w ktrym wpisujemy kolejno: nazw klasy (TLicznik), nazw klasy bazowej
(TEdit), nazw karty, do ktrej chcemy doda@ nasz nowy komponent (pozostawmy Samples).
4. Naciskamy OK. C++ Builder generuje kod szkieletowy klasy komponentu o nazwie TLicznik.
class TLicznik: public TEdit
{
private:
// tu wpiszemy zawarto@ sekcji private (*)
protected:
//tu wpiszemy zawarto@ sekcji protected (**)
public:
//tu wpiszemy zawarto@ sekcji public (***)
__fastcall TLicznikCTComponent* Owner);
__published:
};
5. Zapiszmy nasz komponent w folderze Projects\Komp" z nazw/ Licznik. Musi to by@ ten sam
katalog, ktry dopisalimy do cie1ek przeszukiwaA w punkcie 1.
6. Deklaracja naszej klasy znajduje si w pliku nag=wkowym(Ctrl-F6). Widzimy znajom/
konstrukcj class (...). Wystarczy teraz doda@ w odpowiednich sekcjach w=asnoci i metody
zwi/zane z naszym licznikiem.
7. W tym miejscu umwimy si, 1e dla odr1nienia w=asnoci prywatnych, bdziemy ich nazwy
poprzedza@ przedrostkiem F". W sekcji private dodajemy int FLiczba; (patrz (*)) (zmienna
ca=kowita (int), w ktrej przechowywa@ bdziemy aktualn/ warto@ licznika), char *FBufor,
(wskaEnik na bufor tekstowy (char *) do przechowywania zawartoci okienka edycji).
8. W sekcji protected (patrz(**)) umieszczamy int FRozmiarBuf; (rozmiar bufora tekstowego).
9. W sekcji public (patrz (***)) dodajemy deklaracje metod obs=uguj/cych licznik oraz konstruktor i
destruktor:
void__fastcall Zwieksz(int o_ile); // deklaracja funkcji publicznej
// zwikszaj/cej licznik
void__fastcall Zmniejsz(int o_ile): // deklaracja funkcji publicznej
// zmniejszaj/cej licznik
void __fastcall UstawOnt nowa_wartosc); // deklaracja funkcji
// publicznej ustawiaj/cej now/ // warto@ licznika
_fastcall TLicznikCTComponent* Owner): // konstruktor. Ta pozycja
// powinna ju1 tu by@!
virtual __fastcall ~TLicznik(); // destruktor
S/ to deklaracje metod naszego komponentu, czyli operacji, ktre bdziemy na nim wykonywa@.
S=owo kluczowe void oznacza, 1e funkcja nie zwraca 1adnej wartoci, przez__fastcall
zaznaczamy sposb wywo=ania funkcji. Deklaracja konstruktora (__fastcall TLicznik) powinna
ju1 by@ w sekcji public (nie musimy wic jej dopisywa@). Standardowo jest tam umieszczana
przez program, ktry wytworzy= dla nas szkielet projektu.
10. Przechodzimy w edytorze do karty Licznik.cpp, aby zbudowa@ zawarto@ zdeklarowanych przed
chwil/ metod. Deklaracje umieszczajmy zawsze w plikach nag=wkowych, natomiast
implementacja (zawarto@, algorytm) - to rzecz pliku programu (*.cpp).
11. Sprbujmy zastanowi@ si, jak powinna wygl/da@ funkcja Ustaw. Musi ona wiedzie@", o ile
zwikszy@ licznik (ju1 to za=atwilimy deklaruj/c argument nowa warto./). Musimy nada@ t
now/ warto@ w=asnoci FLiczba, a tak1e zatroszczy@ si o to, by zosta=a ona wywietlona na
ekranie
12. Ostatecznie wpisujemy (w pliku Licznik.cpp" przed konstruktorem _fastcall
TTLicznik::TTLicznik(...)"):
void __fastcall TLicznlk::Ustaw(1nt nowa_wartosc) //metoda ustawiaj/ca
// now/ warto@ licznika
FLiczba-nowa_wartosc; // ustawiamy w=asno@ FLiczba na now/ warto@
itoa(FLiczba,FBufor,l0); //konwersja liczba - tekst przy podstawie 10
SetTextBuf(FBufor); // ustawiamy tekst pola edycji na nasz bufor
};
// tu miejsce na dalsze metody
Tekst komentarza (po znakach //) mo1emy oczywicie sobie darowa@...
W stosunku do deklaracji metody Ustaw w pliku nag=wkowym, dopisalimy do jej nazwy
identyfikator obiektu - TLicznik::Ustaw. Chodzi o wskazanie kompilatorowi, 1e jest to
wewntrzna metoda klasy TLicznik.
13. Teraz pjdzie nam ju1 szybko - wpisujemy poni1ej poprzedniej metody:
void __fastcall TLicznik::Zwieksz(int o_ile) // kod metody publicznej
// zwikszaj/cej licznik
{
Ustaw(FLiczba+o_ile); // wykorzystujemy metod Ustaw do zwikszenia
// 1icznika o o_ile" };
void __fastcall TLicznik::Zmniejsz(int o_ile) // kod metody publicznej
// zmniejszaj/cej licznik
{
Ustaw(FLiczba-o_ile); // wykorzystujemy metod Ustaw
}:
__fastcall TLicznik::TLicznik(TComponent* Owner) // konstruktor - kod
// buduj/cy nasz obiekt
: TEdit(Owner) // konstruktor klasy bazowej
{
FRozmiarBuf=30; // domylny rozmiar bufora
Bufor = new char(FWielkoscBuf); // tworzymy bufor tekstowy dla pola
// edycji
Ustaw(O); // ustawiamy licznik na warto@ domyln/ 0
}
_fastcall TLicznik::~TL1cznik() // "sprz/tamy" po sobie u1ywaj/c
// destruktora
{
delete(FBufor): // zwalniamy bufor tekstowy
}
14. Gotowe! Zapiszmy jeszcze zmiany i mo1emy instalowa@ nasz nowy komponent!
15. W menu Component wybieramy opcje Install. W okienku, ktre si pojawi, wybieramy Add. W
nastpnym okienku wpisujemy Licznik. Teraz jeszcze dwa razy OK. C++ Builder kompiluje
zmodyfikowan/ bibliotek komponentw (VCL) i jeli wszystko wpisalimy bezb=dnie, na karcie
Samples pojawi si nowy komponent - nasz Licznik.
Rysunek 3.1. Instalacja komponentw
16. Mo1emy teraz z niego skorzysta@ w taki sam sposb, w jaki czynilimy to ju1 z innymi
komponentami. Zamknijmy wszystkie pliki (menu File\CIose Ali).
17. Teraz otwieramy nowy projekt (File\New\AppIication). Wybierzmy nasz licznik z karty Samples
i umie@my go na formularzu.
18. Do sterowania licznikiem potrzebne nam bd/ dwa przyciski - Zmniejsz licznik" i .Zwiksz
licznik" (karta Standard\Button). W Inspektorze Obiektw ustawiamy w=asno@ Caption
jednego z nich na Zwiksz licznik", drugiego na Zmniejsz licznik".

przetw_kl(Key,Shift); // wywo=anie naszej funkcji interpretuj/cej
// stan klawiatury
}
5. Jak =atwo si domyle@, odpowiedE na zdarzenie OnKeyUp jest identyczna:
void __fastcali TForml:: FormKeyUpdObject *Sender, WORD &Key,
TShiftState Shift)
{
przetw_kl (Key .Shift);
}
Podsumowanie: zdarzenia OnKeyPress, OnKeyDown i OnKeyUp mog/ by@ bardzo praktycznym i
przydatnym uzupe=nieniem aplikacji. Zw=aszcza w sytuacjach, w ktrych chcemy niezw=ocznie
zareagowa@ na pewn/ kombinacj klawiszy nacinitych przez u1ytkownika (klawisze skrtu).
WyobraEmy sobie chocia1by edytor tekstu, ktry dla zmiany jakiejkolwiek opcji wymaga, aby
u1ytkownik szpera= z mysz/ i klika= bez umiaru w niekoAcz/ce si rozwijane menu, karty, opcje itp.
Dobrze napisana aplikacja powinna udostpnia@ najczciej u1ywane opcje w jak najprostszy sposb.
wiczenie 4.2. Pliki tekstowe i operacje na nich
Zadanie 4.2.1: wykorzystuj/c klas TFileStream skonstruowa@ przegl/dark do plikw
wywietlaj/c/ zadan/ przez u1ytkownika liczb bajtw pliku.
Wykonanie:
1. Zaczniemy od utworzenia nowego projektu (File\New Application).
2. Teraz z karty System wybieramy potrzebne komponenty - DriveComboBox, DirectoryListBox i
FileListBox.
3. Komponenty te s/ zaprojektowane do wsp=pracy ze sob/. Wystarczy ustawi@ w=asno@ DirList
komponentu DriveComboBoxl na DirectoryListBoxl, a w=asno@ FileList komponentu
DirectoryListBoxl na FileListBoxl.
4. Mo1emy ju1 teraz zapisa@ projekt (zad421) i nacieszy@ oczy pierwszymi efektami uruchamiaj/c
nasz nowy program (F9).
5. Dodajmy teraz do naszego projektu komponent Memo (karta Standard), w ktrym bdziemy
wywietla@ zawarto@ plikw, a tak1e pole edycji, w ktrym pozwolimy u1ytkownikowi okreli@
liczb odczytywanych bajtw.
6. G=wnym zadaniem naszego programu borzie reakcja na kliknicie w FileList-Boxl. Program
powinien wtedy otworzy@ wybrany plik, odczyta@ zadan/ liczb bajtw i wywietli@ w polu
Memol. Wszystko to wykona funkcja, ktr/ przypiszemy w=asnoci OnCIick obiektu
FileListBoxl:
void __fastcall TForml::Fi 1eListBoxlClick(TObject *Sender)
{
int RozmiarBuf=Editl->GetTextLen(); // pobieramy informacj o rozmiarze
// tekstu w Editl
// tworzymy w pamici bufor o odpowiednim rozmiarze

char *Bufor=new char[++RozmiarBuf];
Editl->GetTextBuf(Bufor,RozmiarBuf);// pobieramy do bufora zawarto@
// Editl
int LiczbaBajtow=atoi(Bufor); // konwersja tekst - liczba
// tworzymy nowy obiekt TFileStream nadaj/c mu identyfikator "Plik"
//'i otwieraj/c jednoczenie do odczytu (fmOpenRead) plik wybrany
// w FileListBoxl
TFileStream* PIik=new TFi1eStream(File ListBoxl->Fi 1 eName,fmOpenRead);
//tworzymy jeszcze jeden bufor - tym razem na tekst pliku
char *TekstBuf=new charCLiczbaBajtow];
// teraz odczyt zadanej liczby bajtw...
PI ik->Read(TekstBuf,LiczbaBajtow);
//... i jeszcze tylko ustawmy Memol aby "zobaczy=o" odczytany tekst
Memol->SetTextBuf(TekstBuf);
//teraz jeszcze posprz/tamy po sobie...
PIik->Free(); // metoda niszczy obiekt i zwalnia zajmowan/ przez niego
// pami@
delete Bufor; // w stosunku do prostszych struktur alokowanych przez
// new"
deTete TekstBuf; // lepiej jest u1y@ operatora delete"
}
7. Zadanie zosta=o wykonane. Uwa1ajmy jednak, ktre pliki prbujemy otwiera@, gdy1 nasz program
nie jest zbyt odporny na b=dy...
Rysunek 4.1. Przegl/darka plikw tekstowych
Podsumowanie: ten prosty przyk=ad mia= na celu jedynie wskazanie, w jaki sposb mo1na
wykorzysta@ standardowe obiekty C++ Buildera. Warto zwrci@ uwag na wywo=anie konstruktora
klasy TFileStream. Otwiera on plik o nazwie zawartej w pierw-
szym parametrze w trybie okrelonym przez parametr drugi. Jeli chodzi o tryb otwarcia, mamy do
wyboru kilka predefiniowanych wartoci:
fmCreate - utworzenie pliku o podanej nazwie. Jeli plik ju1 istnieje, jest otwierany do zapisu;
FmOpenRead - otwarcie pliku tylko do odczytu;
FmOpenWrite - otwarcie tylko do zapisu (zapis spowoduje zast/pienie starej zawartoci pliku);
fmOpenReadWrite - otwarcie dla modyfikacji zawartoci pliku.
Jeli otwarcie pliku nie powiedzie si, klasa TFileStream wywo=a wyj/tek.
W TFileStream mamy rwnie1 do dyspozycji metod Write umo1liwiaj/c/ zapis i modyfikacj
pliku. Najlepiej poeksperymentowa@. My pjdziemy nieco dalej. Sprbujemy za=o1y@ w=asn/ klas
jako pochodn/ od TFileStream, a potem poszerzy@ nieco jej mo1liwoci w stosunku do
pierwowzoru...
Zadanie 4.2.2: zaprojektowa@ klas pochodn/ w stosunku do TFileStream, ktra (prcz
odziedziczonych mo1liwoci) bdzie w stanie udzieli@ nam dodatkowych informacji o pliku (rozmiar,
atrybuty itp.). Do uzyskania dodatkowych informacji u1y@ funkcji API -
GetFileAttributesByHandle. Utworzy@ testow/ aplikacj wykorzystuj/c/ mo1liwoci nowej klasy.
Wykonanie:
1. Jak zwykle otwieramy nowy projekt.
2. Tworzona przez nas klasa bdzie powa1n/" prac/. Wypada=oby wic zapisa@ j/ w osobnym
module (ang. Unit), tak aby mo1na by=o j/ potem wielokrotnie wykorzystywa@. Utworzymy w
tym celu nowy modu= (FiIe\New Unit) i zapiszemy go jako MFile.cpp".
3. W tym module (oraz jego pliku nag=wkowym) utworzymy now/ klas o nazwie TMFile.
Zaczniemy od definicji (deklaracji) klasy. W pliku nag=wkowym wpisujemy:
class TMFile: public TFileStream // klas wyprowadzamy od TFileStream
{
private: // sekcja "prywatna", tu znajduj/ si chronione dane obiektu
bool FFAArchive; // czy posiada atrybut Archive?
bool FFAHidden; // czy jest ukryty?
bool FFAReadOnly; // czy tylko do odczytu?
DWORD FFSize; // rozmiar pliku (tylko "m=odsza" cz@)
public: // sekcja "publiczna" - tu udostpnimy informacje o pliku
// w za pomoc/ metod
bool __fastcall FAArchive(); // metoda udostpniaj/ca informacje
// o atrybucie Archive
bool __fastcall FAHiddenO; // jw., dla praw dostpu
bool __fastcall FAReadOnly(); // metoda udostpniaj/ca informacje, czy
// mamy do czynienia z plikiem

DWORD __fastcall FSizeO; // zwraca rozmiar pliku
// konstruktor
__fastcall TMFile(const System::AnsiString NazwaPLiku,
unsigned short Tryb);
};
4. Zajmiemy si zawartoci/ operacyjn/" naszej klasy. W pliku TMFile.cpp" wpisujemy cia=a
zdeklarowanych przed chwil/ metod:
// #include - dolaczenie pliku naglwkowego o podanej nazwie
#include <winbase.h> // API - funkcja GetFilelnformationByHandle
// oraz definicja struktury BY_HANDLE_FILE_INFORMATION - warto
tam // zajrzec...
__fastcall TMFile::TMFile(const System::AnsiString NazwaPLiku,
unsigned short Tryb) // konstruktor
:TFileStream(NazwaPLiku,Tryb) // konstruktor klasy bazowej
{
BY_HANDLE_FILE_INFORMATION Filelnf; // struktura zdeklarowana
w pliku winbase.h"
// Handle - wlasnosc TFileStream okreslajaca uchwyt pliku dla
Win API if (GetFi 1 elnformationByHandle((HANDLE)Handle.&Fi1elnf))
{
// jesli pobranie danych o pliku do Filelnf przebieglo w porzadku,
// ustawimy nowe wlasnosci naszej klasy.
// Poniewaz wszystkie atrybuty zawarte sa w kolejnych bitach
// elementu dwFileAttributes - posluzymy sie operatorem "&", aby
// "wyluskac" to, co nas interesuje:
// urzadzenie?
FFAArchive=(Filelnf.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE);
// czy ukryty?
FFAHidden=(FileInf.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
// tylko do odczytu?
FFAReadOnly=(FileInf.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
// informacje o rozmiarze pliku znajduje sie w innym elemencie
// struktury Filelnf
FFSize=Fi1elnf.nFileSizeLow; // mozemy uzyc prostego podstawienia
// jak widac poslugujemy sie struktura Filelnf,
// aby uzyskac kolejne informacje o pliku
}
else // w innym wypadku (bledny uchwyt pliku)
{
MessageBox(0,"PLik nie zostal poprawnie otwarty!"."Blad!",0);
}
}
// reszta metod jest wzglednie prosta - zwracaja po prostu zawartosc
// odpowiedniej wlasnosci
bool __fastcall TMFile::FAArchive()// metoda udostepniajaca
informacje
// o atrybucie Archive
{ return FFAArchive;}// zwracamy wartosc odpowiedniego pola obiektu

bool __fastcall TMFile::FAHiddenC)// metoda udostepniajaca
informacje
// o atrybucie Hidden
{ return FFAHidden;}// zwracamy wartosc odpowiedniego pola
obiektu
bool __fastcall TMFi1e::FAReadOnlyC)// metoda udostepniajaca
informacje
// o atrybucie ReadOnly
{return FFAReadOnly;3// zwracamy wartosc odpowiedniego pola
obiektu
DWORD__fastcall TMFi1e::FSize()// metoda udostepniajaca
informacje
// o rozmiarze pliku
{return FFSize;3// zwracamy wartosc odpowiedniego pola obiektu
5. Nadszed= czas na zbudowanie programu testuj/cego nasz/ klas. Umiecimy go w module
g=wnym (Unitl.cpp").
6. Przechodzimy do projektu formularza. Mo1na do tego celu u1y@ mened1era projektu
(View\Project Manager). Do szybkiego poruszania si wrd wielu modu=w przydadz/ si nam
tak1e gor/ce klawisze:CtrIF12, Shift-F12.
7. Podobnie, jak w poprzednim zadaniu, osadzamy w formularzu komponenty DriveComboBox,
DirectoryListBox i FileListBox.
8. Interakcje midzy tymi trzema obiektami realizujemy tak samo, jak w zadaniu poprzednim
(punkt 3). Aby FileListBoxl wywietla= wszystkie pliki, ktre nas interesuj/, ustawiamy opcje
jego w=asnoci FileType (ftReadOnly, ftHidden, ftSystem, ftArchive) na tnie.
9. Do wywietlenia informacji o pliku w formie zrozumia=ej dla przecitnego miertelnika u1yjemy
zwyczajnego ListBox + jaka etykietka (Label) z napisem Wybrane atrybuty pliku:" - 1eby by=o
jasne, co zawiera lista.
10. Teraz musimy odpowiednio zareagowa@ na kliknicie w obiekt FileListBoxl (u1ytkownik wybiera
plik). W zdarzeniu OnCIick obiektu FiIeListBoxl wpisujemy:
void __fastcall TForml::FileListBoxlClick(T0bject *Sender)
{
// tworzymy nowy obiekt TMFile nadajac mu identyfikator "Plik"
// otwierajac jednoczesnie do odczytu (fmOpenRead) plik wybrany
// w FileListBoxl
TMFile* Plik=new TMFi1e(Fi 1eListBoxl->Fi1eName,fmOpenRead);
// tu zajmiemy sie ustawieniem zawartosci ListBoxl
ListBoxl->Clear(); //usuwamy poprzednia zawartosc
if (PI ik->FAArchive()) // jesli metoda zwrci wartosc true
ListBoxl->Items->Add("Ustawiony atrybut Archive");
else // w innym wypadku...
ListBoxl->Items->Add("Atrybut Archive nie ustawiony");
if (Plik->FAHidden()) // jesli metoda zwrci wartosc true
ListBoxl->Items->Add("Zaznaczony jako ukryty");
else // w innym wypadku...
ListBoxl->Items->Add("Nie jest ukryty");
if (PI ik->FAReadOnly()) // jesli metoda zwrci wartosc true
ListBoxl->Items->Add("Tylko do odczytu!");
else // w innym wypadku...
ListBoxl->Items->Add("Zapis do pliku mozliwy");

// teraz zajmiemy sie odczytem rozmiaru pliku
char Bufor[20];
ltoa(Plik->FSize(),Bufor,10);
AnsiString Rozmiar(Bufor);
ListBoxl->Items->Add("Rozmiar: "+Rozmiar);
11 teraz jeszcze posprzatamy po sobie...
Plik->Free();
}
11. W powy1szym kodzie u1ylimy klasy TMFile. Aby by=a ona widoczna" dla kompilatora (aby
przeanalizowa= jej kod), musimy do=/czy@ plik nag=wkowy tej klasy do modu=u Unitl.cpp". Do
tego celu wykorzystamy dyrektyw kompilatora #include" (do=/cz). Wpiszmy na pocz/tku pliku
Unitl.cpp" pod innymi dyrektywami #include":
#include "MFile.h" // do=/czamy definicj/ klasy TMFile do modu=u // g=wnego
12. Nasz projekt jest gotowy do kompilacji. Zapiszmy go teraz (wszystkie modu=y) w odpowiedniego
katalogu (zad422").
Rysunek 4.2. Przegl/darka atrybutw plikw
Podsumowanie: jeszcze raz wykorzystalimy mechanizm dziedziczenia do zbudowania klasy bardziej
wszechstronnej od swojej poprzedniczki. Warto jednak zwrci@ uwag na co jeszcze. Aby osi/gn/@
zamierzony cel, wykorzystalimy funkcj niskiego poziomu, pochodz/c/ z pow=oki systemowej
Windows (Windows API) -GetFilelnformationByHandle. Gor/co zachcam czytelnikw do
zagl/dnicia do pliku winbase.h", a tak1e innych plikw podkatalogu include\win32". Odkry@ tam
mo1na wiele funkcji daj/cych nowe mo1liwoci. I cho@ wykorzystanie funkcji API nie jest =atwe,
nieraz mo1e si okaza@ jedyn/ drog/ do osi/gnicia zamierzonego celu.
Dokumentacja (niestety - nie zawsze aktualna) na temat funkcji Win 32 API znajduje si w pliku
pomocy win32.hlp" w podkatalogu HELP\MSHELP C++ Buildera. Warto tam zajrze/!
5. Grafika i multimedia w C++ Builderze
Wprowadzenie: trudno przeceni@ znaczenie strony wizualnej aplikacji. Wystarczy uwiadomi@ sobie,
czemu w=aciwie Windows zawdziczaj/ swj sukces... W C++ Builder mamy do dyspozycji wiele
komponentw i metod ukrywaj/cych ca=y szereg nieistotnych z punktu widzenia zastosowaA
szczeg=w. W efekcie programowanie dEwiku i grafiki sta=o si w tym pakiecie tak proste, jak
jeszcze nigdy.
Nasze spotkanie z grafik/ i multimediami w C++ Builderze zaczniemy od obiektu Canvas (p=tno).
Reprezentuje on obszar roboczy komponentu. Mo1emy na nim wykonywa@ r1ne operacje graficzne.
Operuj/c na tym obiekcie mamy do dyspozycji wiele metod i w=asnoci, ktre upraszczaj/ znacznie
wikszo@ wykonywanych operacji graficznych.
wiczenie 5.1. Obiekt Canvas
Zadanie 5.1.1: zbudowa@ program do rysowania po powierzchni formularza oparty na klasie
TCanvas.
Wykonanie:
1. Otwieramy jak zwykle nowy projekt.
2. W pliku nag=wkowym w sekcji public wstawimy deklaracj znacznika ry/owa- I nia bool Rysuj;
3. Zajmiemy si teraz trzonem" naszego programu, czyli odpowiednim zakodowaniem odpowiedzi
formularza na nacinicia myszy. Pod zdarzenie OnMouseDown formularza podpinamy:
// podjedziemy z "pirem" do miejsca, w ktrym nacisnieto
klawisz myszy
Canvas->MoveTo(X,Y);
// ustawiamy znacznik rysowania, aby mysz zostawiala slad
Rysuj=true;
4. Teraz jeszcze reakcja na ruch myszy nad formularzem (OnMouseMove):
if (Rysuj) // jesli ustawiony znacznik rysowania Canvas-
>LineTo(X,Y); // rysujemy linie w kierunku poruszania sie
// myszy (X,Y)
5. Wrazie puszczenia klawisza myszy (OnMouseUp) ustawiamy odpowiednio znacznik rysowania:

// puszczono klawisz - koniec rysowania
Rysuj=false;
6. Moglibymy uzna@, 1e to ju1 koniec! Zapiszmy projekt (zad511). Po uruchomieniu (F9) program
dzia=a! Myl jednak, 1e nie usatysfakcjonuje nas tak prosta aplikacja. Sprbujmy do=o1y@ par
bajerw".
7. Udostpnimy mo1liwo@ rysowania w dowolnym kolorze. Bd/ nam potrzebne trzy komponenty
ScroIlBar i trzy etykiety (Label). Etykietom nadajmy kolejno w=asnoci Caption: Czerwony",
Zielony", Niebieski" i ustawmy je obok kolejnych obiektw ScroIlBar. W=asnoci Max
wszystkich obiektw ScroIlBar ustawmy na 255 (0-255 daje 256 odcieni ka1dego koloru).
8. Teraz musimy jeszcze zdeklarowa@ funkcj, ktra bdzie na podstawie pozycji suwakw ustawia=a
odpowiedni kolor pira (Pen). Nazwijmy j/ UstKoIor. Zdeklarujemy j/ w pliku nag=wkowym
wewn/trz obiektu TForml w sekcji public:
// funkcja "zajmie" sie ustawieniem odpowiedniego koloru na
podstawie
// pozycji suwakw
__fastcall UstKolor(void);
9. Teraz w module g=wnym umieszczamy cia=o nowej funkcji:
__fastcal1 TForml::UstKolor(void)
{
int NowyKolor;//w tej zmiennej wyliczymy nowy kolor
NowyKolor=ScrollBarl->Position; // Zaczynamy od koloru
czerwonego
NowyKolor+=Scrol1Bar2->Position<<8;// Zielony przesuwamy o 8 bitw
// w lewo(8)
NowyKolor+=ScrollBar3->Position<<16;// Niebieski - 16 bitw w
lewo
// Tak oto dostalismy jeden z 16 min kolorw (256*256*256
kombinacji)
Canvas->Pen->Color=NowyKolor; // ustawiamy nowy kolor pira
}
10. Pozosta=o nam jeszcze podpi/@ wywo=ania funkcji UstKoIor w zdarzeniach On-Change suwakw
(UstKolor();) i nasz program jest gotowy. Jeli tylko Windows ma ustawion/ odpowiedni/ palet
kolorw (16 min) - bdziemy mogli nacieszy@ oczy feeri/ barw.
11. Ka1dy szanuj/cy si program graficzny powinien udostpnia@ mo1liwo@ zmiany szerokoci pira
(w=asno@ Width obiektu Pen) - pozostawiam to eksperymentom czytelnikw. Od siebie
doradzam - warto pogrzeba@ w dokumentacji on lin" pod has=em TCanvas. Znajdziecie tam
wiele ciekawych w=asnoci i przyk=adw.
Podsumowanie: szybko (i mam nadziej - bezbolenie) nauczylimy si operowa@ na obiekcie
Canvas, lub (mwi/c uczenie) wykorzystywa@ kontekst urz/dzenia HDC (TCanvas jest jego
reprezentacj/). Poni1ej zajmiemy si czym jeszcze bardziej efektownym - z pliku dyskowego
odczytamy grafik, na=o1ymy j/ na nasz formularz, a na tym wszystkim dopiszemy jeszcze tekst.

Zadanie 5.1.2: utworzy@ projekt, ktry umo1liwi u1ytkownikowi za=adowanie z dysku dowolnej
bitmapy i umieszczenie jej bezporednio w polu formularza. Na bitmap na=o1y@ tekst Test grafiki".
Wykonanie:
1. Bdzie nam potrzebny przycisk z napisem (Caption) Otwrz" oraz okienko dialogowe do
otwierania plikw (OpenDialog - karta Dialogs).
2. OpenDialogl skonfigurujemy tak, aby widzia=" tylko pliki *.bmp (bitmapy). W jego w=asnoci
Filter w pozycji Filter Name wpisujemy ,J3itmapy (*.bmp)'\ a w pozycji Filter- *.bmp".
3. Teraz zajmiemy si przyciskiem Otwrz". Chcemy, aby reakcj/ na kliknicie weA by=o otwarcie
okienka OpenDialogl, a nastpnie otwarcie wybranej przez u1ytkownika bitmapy. Pod zdarzenie
OnClick przycisku podepniemy wic nast- i puj/c/ funkcj:
void __fastcall TForml::ButtonlClickCTObject *Sender)
{
// tworzymy nowy obiekt TBitmap (dodatkowo musimy wskazac
// kompilatorowi, ze chodzi nam o TBitmap z modulu Graphics)
Graphics::TBitmap *Bitmapl=new Graphics::TBitmap();
if (OpenDialogl->Execute()) // jesli uzytkownik wybral bitmape
C
// ladujemy wskazana przez uzytkownika bitmape z dysku
Bitmapl->LoadFromFile(0penDia1ogl->FileName);
// ustawiamy styl pedzla (Brush->Style) na przezroczysty
Bitmapl->Canvas->Brush->Style=bsClear;
// zwiekszamy wysokosc czcionki do 30
Bitmapl->Canvas->Font->Height=30;
// zmieniamy jej kolor na niebieski
Bitmapl->Canvas->Font->Color=clBlue;
// teraz na nasza bitmape nakladamy tekst
Bitmapl->Canvas->Text0ut(10,10,"Test grafiki");
// a tu mai ujemy(Draw) kopie naszej bitmapy w polu formularza
Forml->Canvas->Draw(30,30,Bitmapl);
};
// teraz juz bitmapa nie jest nam potrzebna...
Bitmapl->Free();
}
4. To ju1 wszystko! Zapiszmy nasz projekt (zad512) i mo1emy zacz/@ testowanie -proponuj
images\splash\athena.bmp. atwo zauwa1y@, 1e przy odpowiednio dobranym tle rysunku mo1na
uzyska@ niekonwencjonalne efekty. St/d ju1 tylko krok do utworzenia klasy firmowego"
formularza z logo firmy trwale wkomponowanym w t=o..
.
Rysunek 5.1. Formularz z logo wiczenie 5.2. Komponent TShape
Zadanie 5.2.1: wykona@ program steruj/cy kszta=tem obiektu klasy TShape.
Wykonanie:
1. Po otwarciu nowego projektu przenosimy na formularz obiekt ComboBox (karta Standard) i
oczywicie Shape (karta Additional), przyda si te1 etykieta, za pomoc/ ktrej poinformujemy
u1ytkownika, na jakich w=aciwociach operuj/ pola rozwijanejj listy (ComboBox). Do obiektu
ComboBoxl przypiszemy operacje zwi/zane ze zmian/ kszta=tu obiektu Shapel.
2. Obiekty Shape posiadaj/ w=asno@ Shape, za pomoc/ ktrej mo1na ustawia@ ich kszta=t. Mo1emy
to sprawdzi@ zaznaczaj/c nasz Shapel i wybieraj/c w=asno@ Shape. Wpiszmy ich polskie
t=umaczenia (Ko=o", Elipsa", Prostok/t", Zaokr. Prostok/t", Zaokr. Kwadrat" i Kwadrat")
we w=asno@ Items obiektu Combo-Boxl. Bd/ to jego dozwolone wartoci.
3. Teraz musimy odpowiednio oprogramowa@ zdarzenie OnChange obiektu Com-boBoxl.
Przypisujemy mu nastpuj/c/ funkcj:
void __fastcall TForml::ComboBoxlChange(TObject *Sender)
{
// w zaleznosci od indeksu aktualnej pozycji
// (Itemlndex) zostana wykonane odpowiednie instrukcje
switch (ComboBoxl->ItemIndex)
{
// jesli pierwsza (indeks 0) - ustawiamy wlasnosc
// Shape na stCircle, czyli "Kolo", break oznacza
// przejscie na koniec bloku instrukcji
case 0:Shapel->Shape=stCircle;break;
// 1 - czyli "Elipsa" - itd
case l:Shapel->Shape=stEl1ipse;break;
case 2:Shapel->Shape=stRectangle;break;
case 3:Shapel->Shape=stRoundRect;break;
case 4:Shapel->Shape=stRoundSquare;break;
case 5:Shapel->Shape=stSquare;break;
}
}
4. Pozostaje nam ozdobi@ Combo" odpowiedni/ etykietk/ (Kszta=t") i zapisawszy uprzednio
projekt mo1emy obejrze@ efekty.
Podsumowanie: za pomoc/ obiektu TShape mo1emy w prosty sposb urozmaici@ wygl/d naszej
aplikacji. Nie jestemy ograniczeni tylko do kszta=tu. Obiekty klasy TShape posiadaj/ te1 szereg
innych w=asnoci, takich jak Brush (wype=nienie) czy Pen (piro). Jak zwykle zachcam do
eksperymentw.
wiczenie 5.3. Komponent TImage
Zadanie 5.3.1: zbudowa@ przegl/dark plikw bitmap (*.bmp) korzystaj/c z komponentu Timage.
Wykonanie:
1. Otwieramy nowy projekt i przeci/gamy na formularz komponent Image (karta Additional). Jego
w=asno@ AutoSize (dostosuj rozmiar) ustawiamy na true.
2. Teraz z karty Dialogs pobieramy komponent OpenDialog. Bdzie nam on s=u1y= do wybierania
plikw bitmap. Jego w=asno@ Filter musimy wic ustawi@ odpowiednio na Bitmapy" i *.bmp"
(patrz zadanie 1.5.1 p. 7).
3. Dope=nieniem do kompletu bdzie przycisk, za pomoc/ ktrego u1ytkownik zasygnalizuje nam, 1e
ma ochot na obejrzenie kolejnego obrazka(Button - karta I Standard). Mo1emy mu nada@
napis (Caption) zgodny z jego funkcj/ -Otwrz".
4. Zdarzeniu OnCIick przycisku przypiszmy funkcj:
void __fastcall TForml::ButtonlClick(T0bject *Sender)
{
if (OpenDialogl->Execute()) // jesli zamknieto dialog
potwierdzeniem
Imagel->Picture->LoadFromFile(OpenDialogl->FileName);
// zaladuj z piliku
}
5. Pozostaje nam zapisa@ projekt (zad531) i odda@ si przegl/daniu zawartoci dysku.
Podsumowanie: prcz wymienionych komponent Image posiada szereg innych interesuj/cych
w=asnoci (Center - centruj obraz, Stretch - rozci/gaj). Warto z nimi poeksperymentowa@, aby w
razie potrzeby wiedzie@ jak dopasowa@ ten komponent do swoich potrzeb.

wiczenie 5.4. Funkcja API PlaySound
Zadanie 5.4.1: wykorzysta@ funkcj PlaySound z Win32 API do odtwarzania dEwikw zapisanych
w plikach WAV, ktre wska1e u1ytkownik.
Wykonanie:
1. Bdzie nam potrzebne pole edycji, ktrego u1yjemy do pobrania od u1ytkownika cie1ki i nazwy
pliku.
2. Teraz jeszcze przycisk, ktrego nacinicie zapocz/tkuje odtwarzanie. Pod zdarzenie OnClick
przycisku podpinamy:
#include <mmsystem.h> // dolaczamy plik z definicja funkcji
PlaySound
void __fastcall TForml::ButtonlClick(T0bject *Sender)
{
int RozmiarBuf - Editl->6etTextLen();//Rozmiar tekstu w Editl
char *Bufor = new char[++RozmiarBuf]; // tworzymy odpowiedni Bufor
Editl->GetTextBuf(Bufor, RozmiarBuf);// pobieramy tekst do bufora
PlaySound(Bufor,0,SND_SYNC); // uruchomienie funkcji API
odtwarzajacej
// dzwiek
}
3. Zapiszmy projekt w folderze zad541. Mo1emy przyst/pi@ do testowania programu.
4. Po uruchomieniu w pole edycji wpiszmy C:\WINDOWS\MEDIA\The Microsoft Sound". Czy
rozleg=y si znajome dEwiki? Jeli nie - trzeba sprawdzi@:
czy na dysku znajduje si plik o nazwie podanej w p. 4?
czy macie kart dEwikow/?
czy g=oniki s/ po=/czone kablem z w=aciwym wyjciem tej karty?
czy wtyczka jest w kontakcie?
czy nie macie problemw ze s=uchem?
Jeli nie zachodzi 1aden z tych punktw, a Wy nic nie s=yszycie - to ja si poddaj.
Mo1e to wygl/da@ na 1arty - ale w tym w=anie tonie s/ utrzymane niektre z blokw Usuwania
problemw" systemu pomocy Windows 95.
Na powaTnie: dokumentacj funkcji PlaySound (i nie tylko) znajdziecie w pliku pomocy Win 32 API
win32.hlp", ktry znajdziecie w podkatalogu HELP\MSHELP C++ Buildera. Warto tam
zajrze/!!!
wiczenie 5.5. Obiekt MediaPlayer
Wprowadzenie: z obiektem MediaPlayer mielimy ju1 do czynienia w @wiczeniu 1.5. U1ywalimy
go wtedy do odtwarzania plikw AVI. Jego funkcja jest jednak o wiele szersza. Opera si on na MCI
(Media Control Interface), czyli interfejsie wysokiego poziomu do obs=ugi multimediw w Windows
95 i NT. Mo1emy odtwarza@ nie tylko pliki AVI, ale tak1e WAV czy MIDI. W poni1szych zadaniach
zapoznamy si bli1ej z komponentem MediaPlayer. Zastosujemy jednak nieco inne podejcie: to my
bdziemy decydowa@, do ktrych funkcji MediaPlayer bdzie mia= dostp u1ytkownik. Stanie si to
mo1liwe dziki ukryciu samego komponentu (Visible=false) i udostpnieniu tylko niektrych jego
funkcji. Jak si przekonamy, wcale nie musi to prowadzi@ do zubo1enia aplikacji.

Zadanie 5.5.1: za pomoc/ komponentu MediaPlayer utworzy@ aplikacj, ktra pozwoli
u1ytkownikowi na stworzenie listy plikw multimedialnych (*.wav, *.avi, *.mid), a nastpnie na ich
odtwarzanie w dowolnej kolejnoci.
Wykonanie:
1. Zaczniemy od komponentu ListBox (karta Standard), przy u1yciu ktrego umo1liwimy
u1ytkownikowi stworzenie listy plikw.
2. Nastpnie do naszego projektu dodamy okienko dialogowe OpenDialog (Dialogs). Pos=uguj/c si
nim u1ytkownik bdzie wybiera= interesuj/ce go pliki, ustawmy wic jego w=asno@ Filter:
,Xnimacje(*.avi)"', *.avi"; ,J)ZwiTk(*.wav)", *.wav"; ^liki MIDI(*.mid)", *.mid"; (odtworzenie
tych ostatnich mo1e si nie uda@, jeli nie posiadamy poprawnie skonfigurowanego sekwencera
MIDI).
3.- Teraz jeszcze przycisk z napisem Dodaj". Jego w=asnoci OnClick przypiszemy funkcj:
void __fastcall TForml::ButtonlClick(TObject *Sender)
if (OpenDialogl->Execute())
// dodajemy nazwe wybranego pliku do elementw ListBoxl
ListBoxl->Items->Add(OpenDialogl->FileName);
}
4. Teraz zajmiemy si stron/ wizualno-dEwikow/. Jako powierzchni do wywietlania ewentualnych
obrazw u1yjemy komponentu Panel (karta Standard - ostatni). Rozci/gnijmy go odpowiednio
na formularzu. Teraz komponent MediaPlayer (System). Ustawmy jego w=asno@ Display
(wywietlanie) na Paneli. Poniewa1 nie chcemy, aby by= widoczny, ustawiamy jego w=asno@
Visible na false.
5. Pozosta=o nam odpowiednio zareagowa@ na kliknicie mysz/ w ListBoxl (u1ytkownik wybiera
inny plik). W=asnoci OnClick obiektu ListBoxl przypisujemy:
void __fastcall TForml::ListBoxlClick(TObject *Sender)
{
// wstawiamy w nazwe pliku w MediaPlayer aktualny element ListBoxl
MediaPlayerl->FileName=ListBoxl->Items->Strings[ListBoxl-
>ItemIndex];
// otwieramy plik
Media PIayerl->Open();
// odtwarzanie
MediaPlayerl->Play();
}
6. Gratuluj nastpnej aplikacji! Po zapisaniu projektu mo1emy zaczyna@ zabaw. Poogl/dajmy sobie
na przyk=ad pliki z katalogu FUNSTUFF CD Win95.

Rysunek 5.2. Multimedialna aplikacja
Podsumowanie: warto poeksperymentowa@ z tym komponentem i jego metodami, takimi jak Pause i
Stop, czy zdarzeniami jak OnClick i OnPost(!)CIick. Zastanwcie si, w jaki sposb zrealizowano
odtwarzanie w komponencie Mediaplayer, 1e mo1liwy jest jednoczesny wybr innego pliku. Od
siebie przypomn tylko: zadanie 3.6.1...
wiczenie 5.6. Animacja przy uyciu komponentu TPaintBox
Zadanie 5.6.1: zrealizowa@ prost/ animacj przy u1yciu komponentu PaintBox.
Wykonanie:
1. Wykorzystamy dwa komponenty PaintBox - pierwszy jako bufor, w ktrym bdziemy rysowa@, a
drugi jako w=aciwe pole animacji. Pozwoli nam to unikn/@ dra1ni/cego efektu migotania.
2. Do kompletu potrzebny jest nam jeszcze przycisk, ktrym u1ytkownik zapocz/tkuje animacj. Pod
w=asno@ OnClick przycisku podepniemy funkcj realizuj/c/ wywietlanie:
void __fastcall TForml::ButtonlClick(TObject *Sender)
{
int wysPBl=PaintBoxl->Height;// nie wiem jak Wy, ale ja wole
polskie
// nazwy... int szerPBl=PaintBoxl->Width;

//Liczymy wsplrzedne srodka PaintBoxl
int Y=wysPBl/2; // Y- wysokosc II
int X=szerPBl/2; // X- szerokosc II
int i,j,r[3]=C0,10,203;// inicjujemy tablice 3 promieni kl
TCanvas *PBlCanv,*PB2Canv; // uzyjemy wskaznikw, aby prosciej sie
// kodowalo
PBlCanv=PaintBoxl->Canvas; // wskazujemy na PaintBoxl
PB2Canv=PaintBox2->Canvas; // wskazujemy na PaintBox2
for (i=0;i<30;i++) // 30 klatek animacji
{
PBlCanv->Brush->Style=bsSolid; // styl wypelnienia - pelne
PBlCanv->Brush->Color=clWhite;//wypelnienie biale
// wymazujemy poprzednia zawartosc rysujac prostokat
wypelniony na bialo
PBlCanv->Rectangle(0.0,szerPBl,wysPBl);
PBlCanv->Brush->Style=bsClear; // styl wypelnienia - brak
for(j-0;j<3;j++) // kolejne kola = kolejne promienie
{
r[j]+=3; // zwiekszamy kolejny promien o 3
// Kolo jak wiadomo to szczeglny przypadek elipsy...
PBlCanv->Ellipse(X-r[j],Y-r[j],X+r[j],Y+rCj]);
}
//teraz kopiujemy to, co narysowalismy do PaintBox2
PB2Canv->CopyRect(Rect(0,0.szerPBl,wysPBl),PBlCanv,Rect
(0,0,szerPB.l,wysPBD); }
}
}
3. Zapisawszy projekt mo1emy przyst/pi@ do testw.
Podsumowanie: obraz w PaintBoxl migocze, ale za to w PaintBox2 widzimy bardzo p=ynn/
animacj. Dla porwnania efektw u1ylimy jako bufora roboczego obiektu PaintBoxl. W
prawdziwych" aplikacjach mo1na do tego celu u1y@ obiektu klasy TBitmap stworzonego
dynamicznie w pamici (operator new) bez reprezentacji na ekranie. Jak zwykle polecam to
eksperymentom.

6. Bazy danych w C++ Builderze
wiczenie 6.1. Database Form Wizard
Wprowadzenie: bazy danych to szeroka i bardzo wa1na dziedzina zastosowaA komputerw.
W=aciwie mo1na by rzec, 1e w dzisiejszych czasach ka1dy powa1ny program w mniejszym czy
wikszym stopniu opiera si na bardziej lub mniej zaawansowanych strukturach danych. W
poni1szych @wiczeniach zapoznamy si ze sposobami budowy i obs=ugi baz danych w C++ Builderze.
Rozpoczniemy od rzeczy najprostszej - budowy przegl/darki danych za pomoc/ Database Form
Wizard. Omwimy nastpnie normalizacj (optymalizacj) struktur danych, aby w koAcu przej@ do
omwienia kolejnych mechanizmw usprawniaj/cych obs=ug baz danych.
Zadanie 6.1.1: zaprojektowa@ formularz danych Zwierzaki" przy u1yciu Database Form Wizard i
przyk=adowej tabeli ,,exampIes\animaIs.dbf'.
Wykonanie:
1. Otwieramy nowy projekt (Application).
2. Otwieramy ponownie menu File opcja New, a nastpnie wybieramy z karty Forms Database
Form.
3. W okienku, ktre si pojawi, pozostawiamy domylne opcje Create a simple form" i Create a
form using TTable objects".
4. Naciskamy Next.
5. Wybieramy katalog ...\Examples\DATA\ANIMALS.DBF".
6. Znw Next.
7. Teraz klikamy w przycisk >" przenosz/c kolejne pola tabeli animals" do naszego formularza.
8. Next, Next, Finish - i oto naszym oczom ukazuje si gotowy formularz pod=/czony" do tabeli,
ktr/ wskazalimy, czyli animals".
9. Zapisujemy tradycyjnie w katalogu (Projects\zad611") i uruchamiamy program (F9). '
10. Widzimy wyraEnie, 1e naszej aplikacji czego brakuje. Zauwa1ylimy na pewno pole BMP" - co
niechybnie oznacza rysunek - a my tymczasem niczego nie widzimy.
11. PrzejdEmy z powrotem do projektu (zamknicie aplikacji - X" w prawym grnym rogu okna).
12. Pora na par wskazwek jak to dzia=a". Otwrzmy kart Data Access palety komponentw. Czy
widzimy komponent TTable - drugi z lewej? Identyczny komponent znajduje si u nas na
formularzu Form2. Nazywa si Tablel i reprezentuje tabel animals" (rzu@my okiem na jego
w=aciwo@ TableName).
13. Jako porednik" midzy Tablel (tabel/) a polami edycji s=u1y komponent Data-Sourcel 0
e
g
odpowiednik znajduje si jako pierwszy z lewej na karcie Data Ac-cess'tabeli komponentw).
Przyjrzyjmy si jego w=aciwoci DataSet. Czy nie jest to Tablel?
14. Na koAcu tego =aAcuszka" znajduj/ si tzw. kontrolki danych", czyli obiekty takie jak DBEdit,
DBGrid, DBMemo, DBImage i inne z karty Data Controls. Je1eli podejrzymy w=aciwo@
DataSource obiektw DBEdit z naszego formularza, zauwa1ymy, 1e s/ one pod=/czone z kolei
do Erd=a danych, czyli obiektu Da-taSourcel.
15. Chc/c otrzyma@ obrazek zwierzcia na formularzu musimy u1y@ obiektu DBImage i poprzez
DataSourcel po=/czy@ go z polem BMP tabeli Tablel(animals").
16. W praktyce wygl/da to bardzo prosto. Z karty Data Controls wybieramy obiekt DBImage (6-ty
od lewej). Umieszczamy go na naszym formularzu i ustawiamy jego w=aciwo@ DataSource na
DataSourcel".
17. Teraz pozostaje nam jeszcze wskaza@, o ktre pole tabeli animals" nam chodzi. Ustawiamy
w=aciwo@ DataField na BMP i gotowe - pod=/czylimy rcznie" kontrolk DBImagel do tabeli
danych.
18. SprawdEmy efekty (F9) i zapiszmy ponownie nasz/ aplikacj.
Podsumowanie: nauczylimy si, jak zaprojektowa@ prosty formularz danych przy u1yciu Data Form
Wizard. Co wa1niejsze - wiemy ju1, w jaki sposb kontrolki danych s/ pod=/czone do tabel
(=aAcuszek TabIe<->DataSource<->kontrolka).
Rysunek 6.2. Uproszczony model po=/czenia tabeli i kontrolek danych
wiczenie 6.2. Projekt bazy danych oraz co nieco o normalizacji...
Wprowadzenie: wikszo@ obecnie stosowanych baz danych pos=uguje si modelem relacyjnym.
Opiera si on na tabelach, ktre sk=adaj/ si z wierszy i kolumn. Oto przyk=ad takiej tabeli
zawieraj/cej informacje na temat klientw bli1ej nieokrelonej
firmy:
Rysunek 6.3. Tabela Klienci
W dalszej czci ksi/1ki kolumny tabeli nazywa@ bdziemy polami, a wiersze rekordami (wpisami).
Powy1sza tabela sk=ada si z trzech pl i trzech rekordw.
Liczba kolumn w tabeli jest sta=a. Zmiana liczby kolumn oznacza zmian struktury tabeli. Mo1na to
wykona@ za pomoc/ specjalnych narzdzi (Database Desktop).
Podczas projektowania struktury tabeli dla ka1dej kolumny (pola) okrelamy typ i d=ugo@ informacji
w niej przechowywanej. W formacie XBase (czyli popularnych DBF-ach) przewidzianych jest pi@
typw pl:
C - (Character) tekstowy
N - (Number) numeryczny
L - (Logical) logiczny
D - (Dat) daty
M- (Memo) notatnikowy
Rozmiar pola (Size) okrela si w znakach (bajtach). Dla pl typu L i D d=ugo@ jest ustalona i
niezmienna.
Dla typu N mo1na okreli@ dodatkowo liczb miejsc po przecinku (Dec). Jest ona wliczana do
rozmiaru pola.
Warty wzmianki jest typ M. Ograniczenie rozmiaru pola typu C nie pozwala umieszcza@ w nim
d=u1szych tekstw. Pola typu M pozwalaj/ na do=/czanie do wierszy (rekordw) tabeli notatek o
nieograniczonej d=ugoci. Pole tego typu ma w tabeli d=ugo@ dziesiciu znakw i stanowi odsy=acz do
tekstu notatki. Warto podkreli@, 1e brak notatki powoduje zu1ycie jedynie 10-ciu znakw na
niewykorzystany odsy=acz.
Rozszerzeniem standardu s/ dodatkowe typy pl dostpne w dBASE for Windows: Float, OLE i
Binary.
Indeksy
Indeks jest posortowanym (uporz/dkowanym) zbiorem wartoci danego pola po=/czonych z
odnonikami do danych w tabeli. Na przyk=ad dla naszej tabeli Klienci indeks rosn/cy wed=ug pola
IMIj mg=by wygl/da@ tak:
Rysunek 6.4. Indeks rosn/cy po polu IMIj
Wprzyk=adzie mamy do czynienia z sytuacj/ bardzo prost/ - s/ tylko trzy rekordy. Nie by=oby
jednak ju1 tak prosto, gdyby tych rekordw by=o powiedzmy 100. Znalezienie dowolnego nazwiska
bez u1ycia indeksu wymaga=oby rednio 50-ciu porwnaA. Przy u1yciu indeksu, czyni/c to metod/
przeszukiwania binarnego (strzelamy" w rodek indeksu, porwnujemy, a potem znw strzelamy" w
rodek tego, co pozosta=o), mamy wynik po najwy1ej 7-miu porwnaniach!
Aliasy
Tabele organizuje si w wiksze struktury na wiele r1nych sposobw. Microsoft Access
przechowuje wszystkie tabele wraz z indeksami w jednym pliku. W XBasei Paradox ka1da tabela i
indeks to oddzielne pliki. Dla ujednolicenia zasad po=/czenia si z dowolnym rodzajem bazy danych
wprowadzono pojcie aliasu. Alias jest zastpcz/ nazw/ bazy, wszelkie dodatkowe informacje s/
przed nami ukryte w jego wntrzu. Z poziomu aplikacji pos=ugujemy si po prostu jego nazw/
niezale1nie od tego, z ktrej bazy danych korzystamy.
Klucze
Model relacyjny nakazuje, aby ka1dy wiersz tabeli by= unikatowy. Nieprzestrzeganie tej
zasady stwarza wszelkiego rodzaju niejednoznaczne sytuacje i problemy, ktrych najlepiej unika@.
Klucz jest polem, ktre identyfikuje jednoznacznie dany rekord (wpis). Jest to tzw. klucz g=wny
(pierwotny) tabeli. Klucze pomagaj/ bezpieczniej pos=ugiwa@ si tabel/.
W wielu sytuacjach na klucz g=wny najlepiej obra@ jaki arbitralny, niezmienny numer. W
naszej przyk=adowej tabeli na klucz g=wny wietnie nadaje si pole ID_KLIENTA. Dziki temu polu
mo1emy jednoznacznie zidentyfikowa@ rekordy. Nawet jeli pojawi si kto o identycznym imieniu i
nazwisku istniej/cym ju1 w tabeli (np. Sk/py Tadeusz 2), nie wprowadzi to w nasze dane zamtu.
Mog/ tak1e w tabeli wystpowa@ pola nazywane kluczami zewntrznymi lub obcymi. Pole
klucza zewntrznego mo1e w innej tabeli funkcjonowa@ jako klucz g=wny. Mo1emy dziki temu
=/czy@ tabele w logiczne zwi/zki nazywane relacjami. Odzwierciedlaj/ one rzeczywiste powi/zania
miedzy przechowywanymi w r1nych tabelach danymi.
Relacje
Istniej/ r1ne rodzaje relacji midzy tabelami: 1 do 1, 1 do N i N do N. Przyjrzymy si im
kolejno. Relacje midzy polami tej samej tabeli zwykle mo1emy uzna@ za 1 do 1 (j
ecm
ej wartoci
danego pola odpowiada jedna warto@ nastpnego). Niekiedy zachodzi sytuacja, gdy chcemy tak/
tabel podzieli@ na dwie mniejsze. Wtedy relacja midzy nimi te1 bdzie typu 1 do 1.
Najczstszym typem relacji jest 1 do N. Mamy z nim do czynienia w poni1szym przyk=adzie,
gdzie tabela Klienci poprzez pole ID_KLIENTA zosta=a zwi/zana z tabel/ zamwieA (Zamw).
Wida@ wyraEnie, 1e klientowi o ID=3 (Sk/py Tadeusz) odpowiadaj/ 2 zamwienia (z 21 i 23 lipca).
Relacj 1 do N mo1emy najprociej zrealizowa@ za pomoc/ w=asnoci Master-Source i
MasterFields komponentu TTable (w dalszej czci omwione zostanie to dok=adniej).
Istniej/ tak1e relacje typu N do N. Jest to jednak rozwi/zanie bardzo skomplikowane. W
praktyce rozwi/zuje si takie problemy tworz/c dwie relacje 1 do N korzy-staj/ce z tabeli poredniej.
Jako przyk=ad mo1na by tu poda@ baz danych na temat ksi/1ek i ich autorw. Ksi/1ka mo1e mie@
kilku autorw, ale i autor, rzecz jasna, mo1e napisa@ kilka ksi/1ek. Jeli chcemy obie te sytuacje
poprawnie obs=u1y@, musimy zbudowa@ baz poredni/, w ktrej spotkaj/ si ID_KSIA]KI i
ID_AUTORA.
Projekt bazy danych
Zaprojektowanie dobrej bazy danych wymaga znalezienia jak najlepszego sposobu
odwzorowania struktur i powi/zaA pochodz/cych ze wiata rzeczywistego w abstrakcyjnym modelu
sk=adaj/cym si z kolumn, tabel i zwi/zkw midzy nimi. Dobry projekt, budowa i dopracowanie
bazy danych wymaga wiele czasu i wysi=ku. Jednak1e warto w wysi=ek w=o1y@, aby otrzyma@
solidny fundament w postaci efektywnych i elastycznych struktur danych.
Przy projektowaniu bazy danych dokonujemy szeregu wyborw. Ile ma by@ tabeli? Co maj/
zawiera@? Z jakich kolumn maj/ si sk=ada@? Jakie powinny zachodzi@ midzy nimi zwi/zki?
Odpowiedzi na ka1de z tych pytaA udzieli nam procedura nazywana normalizacj/ baz danych. Celem
normalizacji jest uzyskanie optymalnej struktury danych.
Teoria normalizacji okrela tzw. postacie normalne, stosowane w trakcie optymalizacji.
Kolejne postacie normalne s/ zespo=em coraz ostrzejszych warunkw, ktre struktura danych musi
spe=ni@. W trakcie normalizacji tabele s/ przekszta=cane zgodnie z tymi regu=ami a1 do osi/gnicia
zadowalaj/cego kszta=tu. Najwiksze znaczenie praktyczne maj/ pierwsze trzy postacie normalne i to
na nich skupimy si w poni1szych zadaniach.
Zadanie 6.2.1: zaprojektowa@ struktur danych dotycz/cych klientw pewnej firmy, zamwieA przez
nich sk=adanych, oraz przedmiotw tych zamwieA - produktw firmy.
Wykonanie:
1. Najprociej bdzie bez zbdnych rozmylaA zapisa@ po prostu obok siebie rodzaje informacji,
ktre chcemy przechowywa@.
2. Pierwsza rzecz, ktra nam z pewnoci/ przyjdzie do g=owy, to dane na temat klientw firmy:
ID_KLIENTA (numer kolejny), NAZWISKO i IMI^.
3. Rzecz nastpna - informacje na temat sk=adanych zamwieA: ID_ZAMOWIENIA (kolejny
numer) i RZECZY (szczeg=y dotycz/ce zamwienia).
4. Zestawmy obok siebie wybrane przez nas dane:

Pierwsza posta/ normalna
Zadanie 6.2.2: znormalizowa@ zaprojektowan/ struktur do pierwszej postaci normalnej (1PN).
1. Pierwsza posta@ normalna (1PN) wymaga, aby wszystkie wartoci kolumn by=y elementarne.
Oznacza to, 1e w jednej kolumnie mo1e znajdowa@ si tylko jedna warto@, a nie lista wartoci.
Pole RZECZY posiada zbyt du1o wartoci. Powoduje to trudnoci w operowaniu na nich.
Wystarczy sobie wyobrazi@, 1e chcemy uzyska@ zestawienie liczby sprzedanych produktw...
2. Uzyskanie informacji z takiej tabeli jest bardzo k=opotliwe (o ile w ogle mo1liwe -prosz
przyjrze@ si wpisom 3 siekiery" i 1 siekiera"...). Musimy zatem zmodyfikowa@ nieco nasz/
tabel i zorganizowa@ j/ troch inaczej.
Tabela 2
3. Tabela 2 rozwi/zuje nasz problem zwi/zany z niepodzielnoci/ kolumn, ale nie jest to jeszcze
pierwsza posta@ normalna, ktra wyklucza powtarzanie si tych samych grup danych w
kolumnach. Grupami, ktre si u nas powtarzaj/, s/ ILO_CI i ILOSC2 oraz PRODUKT1 i
PRODUKT2.
4. G=wn/ wad/ tego rozwi/zania jest ograniczona pojemno@ tabeli. Ju1 teraz zabrak=o nam miejsca,
aby wpisa@ toporek z zamwienia nr 2. Dopisanie kolejnych kolumn ILOSC i PRODUKT do
niczego nie prowadzi. W ten sposb nasza tabela rozrasta=aby si do znacznych rozmiarw.
Kolejna niedogodno@ polega na koniecznoci przeszukiwania kilku kolumn (np. ILO_CI,
ILOSC2 itd.). Musimy zatem tak zmodyfikowa@ tabel, aby wyeliminowa@ problem
powtarzaj/cych si kolumn.

5. W tabeli 3 pojawi=a si nowa kolumna NR_POZYCJI. Okrela ona pozycj w zamwieniu.
Zabieg ten spowodowa=, 1e tabela znajduje si w pierwszej postaci normalnej, a zatem nie ma w
niej powtarzaj/cych si kolumn, a ka1da kolumna zawiera tylko jedn/ warto@. Kluczem g=wnym
jednoznacznie identyfikuj/cym rekordy jest z=o1enie kolumn ID_ZAMOWIENIA i
NR_POZYCJI.
Druga posta/ normalna
Zadanie 6.2.3: znormalizowa@ powy1sz/ struktur do drugiej postaci normalnej.
Wykonanie:
1. Tabela ma drug/ posta@ normaln/ (2PN), je1eli jest w 1PN, a ka1da kolumna, ktra nie nale1y do
1adnego klucza potencjalnego, jest ca=kowicie zale1na od ca=ego klucza g=wnego. Inaczej -
rekordy tabeli powinny przechowywa@ dane dotycz/ce tylko jednego obiektu (zdarzenia,
jednostki) i obiekt ten powinien by@ identyfikowany przez ca=o@ klucza g=wnego.
2. W naszej tabeli kluczem g=wnym jest z=o1enie kolumn ID_ZAMOWIENIA oraz |
NR_POZYCJI. Aby tabela by=a w 2PN, ka1da niekluczowa kolumna (czyli ka1da prcz
ID_ZAMOWIENIA i NR_POZYCJI) musi w pe=ni zale1e@ od z=o1enia kolumn
ID_ZAMOWIENIA i NR_POZYCJI. Warunek ten nie jest spe=niony. Wystarczy pole
ID_ZAMOWIENIA (a wic tylko cz@ klucza g=wnego), aby w pe=ni zidentyfikowa@ warto@
pola ID_KLIENTA (a wic tego, kto to zamwienie z=o1y=). Z tego powodu tabela nie jest w 2PN.
3. Konieczne jest rozbicie tabeli na dwie mniejsze. W pierwszej z nich znajd/ si szczeg=y
dotycz/ce zamwieA (a wic pola zale1ne od czci klucza g=wnego ID_ZAMOWIENIA). W
drugiej znajd/ si informacje zwi/zane z artyku=ami okrelonymi w zamwieniu (pola zwi/zane
zarwno z ID_ZAMOWIENIA, jak i NR_POZYCJI).

4. Warto zauwa1y@, 1e przechodz/c do kolejnej postaci normalnej nie pozbylimy si 1adnej kolumny,
1adnych danych (wrcz przeciwnie - dodalimy pole DATA_ ZAMWIENIA). Ponadto tabele
zosta=y tak podzielone, 1e mo1na je =atwo z=o1y@ za pomoc/ pola ID_ZAMOWIENIA. Pole to pe=ni
rol klucza g=wnego w tabeli 4, a obcego - w tabeli 5.
Trzecia posta/ normalna
Zadanie 6.2.4: znormalizowa@ struktur tabel do trzeciej postaci normalnej (3PN).
Wykonanie:
1. Tabela jest w trzeciej postaci normalnej, jeli jest w 2PN, a wszystkie kolumny, ktre nie nale1/ do
1adnego klucza potencjalnego, s/ wzajemnie niezale1ne.
2. W poprzednim zadaniu otrzymalimy dwie tabele. Tabela 4 znajduje si ju1 w 3PN, poniewa1 nie
ma tam 1adnych zale1noci pomidzy kolumnami pozostaj/cymi poza kluczem g=wnym.
3. Nie mo1na tego jednak powiedzie@ o tabeli 5. Posiada ona dwie kolumny, ktre nie nale1/ do
klucza i s/ cile od siebie zale1ne - ID_PRODUKTU i NAZWA_

PRODUKTU. Zale1noci takie stwarzaj/ problemy przy dodawaniu, aktualizacji i usuwaniu
rekordw. Je1eli chcielibymy nanie@ np. 10 wpisw dotycz/cych siekier, w ka1dym z tych rekordw
musielibymy wprowadzi@ ID_PRODUKTU (4) i NAZWA_PRODUKTU (siekiery). Jeszcze gorzej
by=oby, gdyby si okaza=o, i1 trzeba zmieni@ opis produktu np. na siekiera dwuostrzowa" (hm..., czy
istnieje co takiego?).
4. Zale1no@ midzy ID_PRODUKTU i NAZWA_PRODUKTU mo1na zalegalizowa@"
doprowadzaj/c do podzia=u Tabeli 5.
5. Nasze tabele (4, 6 i 7) osi/gn=y 3PN, co w praktyce oznacza zadowalaj/c/ efektywno@ ich
struktury.
wiczenie 6.3. Implementacja projektu z wiczenia 6.2
Wprowadzenie: pora na kolejn/ ods=on. Pakiet C++ Builder zawiera bardzo po1yteczne narzdzie -
Database Desktop. Z jego pomoc/ mamy mo1liwo@ tworzy@, przegl/da@, sortowa@ i modyfikowa@
r1norodne tabele baz danych, pocz/wszy od baz Paradox, poprzez dBASE, na zapytaniach SQL
skoAczywszy. Jeli podczas instalacji pakietu pozostawilimy domyln/ lokalizacj, powinnimy
znaleE@ Database Desktop w menu Start (Programy->Borland C++ Builder->Database Desktop)
lub w katalogu Program Files\Borland\Database Desktop\dbd32.exe.
W prosty sposb mo1emy to narzdzie do=o1y@ do wewntrznego menu Tools w C++ Builderze.
Wystarczy wybra@ Tools->Configure->Add->Browse, nastpnie odnaleE@ dbd32.exe, zatwierdzi@
wybr i wybra@ jaki tytu= dla narzdzia (zalecam wpisa@ oryginaln/ nazw - ,J)atabase Desktop").
Jeszcze tylko Close i po ponownym wybraniu menu Tools zobaczycie now/ opcj - nasz Database
Desktop. Mo1emy teraz korzysta@ z niego, tak jak z innych opcji IDE.
Zanim jednak zaczniemy budowa@ kolejne tabele, bdziemy zmuszeni pogrzeba@ nieco w konfiguracji
BDE (Borland Data Engine - t=umacz/c dos=ownie: Silnik Baz Danych firmy Borland). Wybieramy
menu Start->Programy->Borland C++ Builder a>BDE Configuration. Na karcie Drivers
wybieramy z listy Driver Name pozycj DBASE, a nastpnie po prawej, w pozycji LANGDRIVER
wybieramy dBASE PLK cp852. W=anie zmienilimy domyln/ stron kodow/ na rodzim/. Oznacza
to, nie mniej nie wicej, tylko tyle, 1e nie powinnimy mie@ problemw z polskimi literami (/e=@
itp.)
Zadanie 6.3.1: zaprojektowa@ struktur danych z @wiczenia 6.2 wykorzystuj/c Database Desktop.
Wykonanie:
1. Otwieramy menu Tools->Database Desktop (patrz Wprowadzenie).
2. Teraz utworzymy now/ tabel danych poprzez File->New->Table. Poniewa1 jestem przywi/zany
do starego dobrego XBase, wybierzmy w okienku Create Ta-ble typ tabeli dBASE for
Windows.
3. Widzimy przed sob/ projekt struktury nowej tabeli. Wpisujemy w kolejne pozycje Field Name
(nazwa pola-kolumny), Type (typ pola-kolumny), Size (rozmiar), Dec (liczba miejsc po przecinku
- tylko dla pl numerycznych):
ID_KLIENTA", N", 6" (klucz glwny tabeli Klienci)
NAZWISKO", C", 30"
IMIj", C", 20"
4. Mo1emy powy1sz/ struktur uzna@ za uproszczon/ tabel Klienci. Zapisujemy tabel (Save As)
jako Klienci.dbf w katalogu Projects\Dane (jeli brak takiego -utwrzmy go).
5. Naniemy do naszej tabeli przyk=adowe dane. W DBD wybieramy File->Open ->TabIe, a
nastpnie z odpowiedniego katalogu tabel Klienci.dbf. Tabela pojawi=a si na ekranie. Jest
jednak zupe=nie pusta (nic dziwnego - utworzylimy przecie1 jedynie jej struktur).

Rysunek 6.6. Projektowanie struktury tabeli danych
6. Wybieraj/c TabIe->Edit Data wprowadzamy tabel w stan edycji. W kolejne pola (kolumny)
naniemy:
1", IksiAski", Ksawery"
2", Krpy", Zenobiusz"
3"', Sk/py", Tadeusz"
Nie przejmujmy si, jeli pod DBD polskie litery nie bd/ wygl/da@ najlepiej (cho@ mo1emy to
zmieni@ wybieraj/c Edit->Preferences->karta General->Default system font->Change.. i
jak/kolwiek czcionk o nazwie koAcz/cej si na CE).
7. Czas na kolejn/ tabel. Zamykamy tabel Klienci.dbf i w taki sam sposb jak w punkcie 2
tworzymy struktur:
ID_ZAM0W", N", 6" (klucz g=wny tabeli Zamw)
ID_KLIENTA", N", 6" (klucz zewntrzny tabeli Klienci)
DATA_ZAMOW", D"
8. Zapisujemy nasze dzie=o jako tabel Zamow.dbf w katalogu Dane.
9. Postpuj/c podobnie jak w punktach 5-6, nanosimy:
1", 2", 1997.07.20"
2", 3", 1997.07.21"
3", 3", 1997.07.23"

10. Teraz jeszcze struktura szczeg=w zamwieA (File->New->Table):
ID_ZAM0W", N", 6" (klucz zewntrzny tabeli Zamw)
NR_K0L", N", 4"
ILOSC", N", 6"
ID_PRODUKT", N", 6" (klucz zewntrzny tabeli Produkt)
11. Zapisujemy jako ZamowSc.dbf.
12. Wprowadzamy dane (File->Open->Table):
1" 5" 1" 1"
1 12" 2" 2"
2 3 1 4
2 6 2 3
2" 1" 3" 5"
3", 1" 1" 4"
13. Na koniec struktura danych dotycz/cych produktw (ze wzgldu na prostot przyk=adu nieco
ograniczona):
ID_PR0DUKT", N", 6" (klucz g=wny tabeli Produkt)
NAZWA_PR0D", C", 30"
14. Zapisujemy jako Produkt.dbf.
15. Wprowadzamy dane:
1", ganice"
2", w1e stra1ackie"
3", m=otki"
4", siekiery"
5", toporki"
16. To by by=o na tyle - dysponujemy przyk=adow/ baz/ danych zamwieA.
Zadanie 6.3.2: przygotowa@ rcznie formularz do obs=ugi struktury z zadania 6.3.1.
1. Otwieramy nowy projekt. Poniewa1 (w odr1nieniu od przyk=adu z @wiczenia 6.1) struktura naszej
bazy danych nie jest najprostsza, najlepiej bdzie do przechowywania" danych u1y@ osobnego
modu=u - tzw. Data Module. Otwieramy File ->New->Data Module. Za pomoc/ Inspektora
Obiektw nadajemy mu nazw (Name) ,J)ane". Tutaj osadzimy reprezentacje kolejnych tabel.
2. Z karty Data Access wybieramy Table i osadzamy w module Dane. Nadajemy mu nazw (Name)
TKlienci".
3. Sprbujemy w tym miejscu uniezale1ni@ si troch od konfiguracji BDE. Zapiszmy nasz projekt
w katalogu Projects\zad632. Teraz mo1emy ustawi@ w=asno@ DatabaseName nie na Alias, lecz
na cie1k do danych. Wpiszmy wic ,,..\dane" (cie1ka wzgldna) lub C:\(...)\Projects\Dane"
(cie1ka pe=na - bezwzgldna).wzgldna). Nastpnie klikamy we w=asno@ TableName (nazwa
tabeli). Wybierzmy z listy Klienci.dbf.
4. Z karty DataAccess wybieramy teraz komponent DataSource. Jego w=asno@ Name ustawiamy
na SKlienci". We w=asnoci DataSet wybieramy TKlienci.
5. Powtarzamy czynnoci z punktw 2-4 dla kolejnych tabel (Zamw, ZamowSc, Produkt) nadaj/c
obs=uguj/cym je komponentom odpowiednie nazwy (TZamow" i ,JSZamow", TZamowSc" i
,SZamowSc" oraz TProdukt" i ,jSProdukt").
6. Modu= danych mamy ju1 gotowy. Musimy teraz do=/czy@ go do modu=u g=wnego. Przechodzimy
do formularza g=wnego (Shift-F12 lub View->Project Ma-nager->Forml). Z menu File
wybieramy Include Unit Hdr a nastpnie Unit2. Obiekty w module Dane bd/ teraz widoczne
dla naszego formularza g=wnego.
7. Z karty Data Access pobieramy kolejne komponenty DBGrid (pierwszy z lewej) ustawiaj/c ich
w=asnoci DataSource na Dane->SKIienci, Dane->SZamow, Da-ne->SZamowSc i Dane-
>SProdukt.
8. PrzejdEmy teraz jeszcze do modu=u Dane i ustawmy w=asno@ tabel (Table) Ac-tive na true. Na
formularzu g=wnym powinny pojawi@ si dane wpisane przez nas w poprzednim zadaniu.
9. Dodajmy obok kolejnych DBGrid odpowiednie etykiety (,J(lienci", namwienia", Szczeg7y
zamwieK", produkty"). KoAcowy efekt powinien by@ mniej wicej taki:
Podsumowanie: trzeba przyzna@, 1e wygl/da to raczej na ba=agan ni1 powa1n/ aplikacj. Gdybymy
chcieli dowiedzie@ si na przyk=ad kto z=o1y= zamwienie nr 2, musielibymy w=asnorcznie szuka@
odpowiedniego ID_KLIENTA (3) w tabeli Klienci. Zapewne odnajdziemy bez trudu pana Sk/pego,
ale co zrobi@ jeli rekordw (wpisw) w tabeli s/ setki? Mo1emy wtedy mie@ pot1ne trudnoci z
po=/czeniem naszej bazy danych w logiczn/ ca=o@. Dzieje si tak dlatego, 1e zapomnielimy o
zwi/zkach midzy tabelami. A s/ one rwnie wa1ne, jak sama struktura tych tabel.
W poni1szym zadaniu w=o1ymy w nasz/ aplikacj nieco inteligencji. Zmusimy j/, aby wywietla=a
tylko te informacje, ktre nas w danym momencie interesuj/. Jeli wybierzemy jakie konkretne
zamwienie, powinnimy widzie@ tylko list zwi/zanych I z nim produktw i dane jednego klienta -
tego, ktremu nale1y dostarczy@ towar.
Zadanie 6.3.3: zmodyfikowa@ projekt z zadania 6.3.2, tak aby wywietla= wybirczo dane tabel.
Wykonanie:
1. Otwrzmy projekt z zadania 6.3.2 i zapiszmy go w folderze zad633.
2. Zapiszemy teraz zwi/zki midzy tabelami za pomoc/ C++ Buildera. Naj=atwiej bdzie nam
pos=u1y@ si w tym celu w=asnoci/ MasterSource i MasterFields komponentw klasy TTable.
Zaczniemy od powi/zania tabel Zamw i Klienci relacj/ wed=ug pola ID_KLIENTA
(zauwa1my, 1e w pierwszej tabeli jest ono kluczem zewntrznym, w drugiej - g=wnym). Aby
dokona@ takiego powi/zania, pole ID_KLIENTA tabeli szczeg=owej (Klienci) musi by@
zindeksowane. Za=o1ymy indeks za pomoc/ Database Desktop.
3. Aby mo1na by=o zindeksowa@ tabel, bdziemy musieli zamkn/@ j/ pod C++ Buil-derem. W
naszym projekcie w module Dane ustawiamy w=asno@ TKlienci ->Active na false.
4. W DBD (Database Desktop) uruchamiamy File->Open->Table. Nastpnie otwieramy tabel
Projects\Dane\Klienci.dbf.
5. Wybieramy Table->Restructure i upewniamy si, czy w rozwijanym polu edycji Table
properties jest ustawiona pozycja Indexes. Naciskamy Define, w polu listy klikamy w
ID_KLIENTA, nastpnie zaznaczamy Mantained (wewntrzny) i wybieramy OK. Jeszcze raz
OK (zatwierdzamy nazw domyln/ ID_KLIENTA).
6. Teraz jeszcze poprzez Save zapiszmy zmiany. Mo1emy przej@ do naszego projektu i z powrotem
ustawi@ w=asno@ TKIienci->Active na true.
7. Na tym jeszcze nie koniec. Dodalimy dopiero indeks ID_KLIENTA do tabeli Klienci. Musimy
teraz po=/czy@ tabel Klienci z tabel/ Zamw. Wykorzystamy do tego celu jej w=asno@
MasterSource (Erd=o danych tabeli nadrzdnej) i MasterFields (pole klucza zewntrznego tabeli
nadrzdnej).
8. We w=asnoci MasterSource obiektu TKlienci wybieramy SZamow. Klikamy dwa razy we
w=asno@ MasterFields. Pojawi si dialog o nazwie Field Link Designer. S=u1y on do po=/czenia
obu tabel za pomoc/ wybranych pl.
Rysunek 6.8. FieldLink Designer
9. W pozycji Available Indexes wybieramy ID_KLIENTA. Teraz w obu listach (Detail Fields i
Master Fields) wybieramy ID_KLIENTA i naciskamy Add (dodaj po=/czenie). W licie Joined
Fields powinien pojawi@ si napis JD KLIENTA->ID KLIENTA". Naciskamy OK.
10. Mo1emy uruchomi@ nasz program i obejrze@ efekty. Kiedy poruszamy si po ta- I beli ZamwieA,
w tabeli klientw widoczne s/ rekordy odpowiadaj/ce aktualnemu zamwieniu. W pozosta=ej
czci aplikacji nadal jednak panuje ba=agan.
11. Powtarzaj/c czynnoci z punktw 3-6 zak=adamy kolejne indeksy ZamowSc.dbf ->ID_ZAMOW
i Produkt.dbf->ID PRODUKT (pamitajmy o zmianie w=aciwoci Active kolejnych tabel na
false, a potem na true).
12. Teraz pod C++ Builderem ustawiamy w=asnoci TZamowSc->MasterSource na SZamow, a
TProdukfMasterSource na SZamowSc. We w=asnoci MasterFields obiektu TZamowSc
=/czymy pola ID_ZAMOW, a obiektu TProdukt pola ID PRODUKT (patrz p. 8-9).
13. Po uprzednim zapisaniu mo1emy uruchomi@ aplikacj.
Podsumowanie: aplikacja wygl/da teraz du1o lepiej. Informacje s/ ze sob/ logicznie powi/zane. Jeli
wybierzemy jakie zamwienie, otrzymujemy informacje na jego temat - I dane klienta, ktry je z=o1y=
i szczeg=y zamwienia. Poruszaj/c si z kolei po szczeg=ach zamwienia mo1emy obejrze@ nazwy
produktw, ktrych zamwienie dotyczy.

wiczenie 6.4. Kontrola danych
Wprowadzenie: uLytkownik aplikacji nanoszSc do niej dane czpsto wyprawia rzeczy, na ktrych widok
jej projektant obla=by si zimnym potem. Nie jestemy w stanie przewidzie@ wszystkich mo1liwych (i
niemo1liwych) poczynaA u1ytkownika. Istniej/ jednak sposoby, aby cho@ w czci ograniczy@
mo1liwo@ powstania b=dw w danych. Poni1ej przedstawimy dwie podstawowe techniki: poprzez
maski wprowadzania (EditMask, EditFormat) i zatwierdzanie zmian (zdarzenie OnValidate).
Zadanie 6.4.1: utworzy@ program kontroluj/cy poprawno@ danych wpisywanych przez u1ytkownika
do tabeli Klienci.dbf.
Wykonanie:
1. Otwieramy projekt nowej aplikacji (File->New Application).
2. Nasz przyk=ad bdzie bardzo prosty, nie bdziemy wic zawraca@ sobie g=owy osobnym modu=em
dla danych. Z karty Data Access pobieramy DataSource i TTable.
3. W=asno@ DatabaseName mo1emy ustawi@ podobnie jak w zadaniu 6.3.2 p.4, lecz mo1emy te1
utworzy@ nowy Alias otwieraj/c Start->Programy->Borland C++ Builder->BDE
Configuration. Wybieramy nastpnie kart AIiases->New Alias. Pojawi si okienko z
zapytaniem o nazw i typ nowego aliasu. W pole New alias name wpisujemy np. CB_TEST i
pozostawiamy typ STANDARD. Teraz po prawej stronie, w rubryce Parameters w pozycji
PATH wpisujemy pe=n/ cie1k do danych (katalog C++ Buildera + Projects\Dane), a w pozycji
DEFAULT DRIVER wybieramy DBASE. Tak oto skonfigurowalimy nowy alias, ktrego
bdziemy mogli teraz u1ywa@ dla wskazania tabel XBase znajduj/cych si w katalogu
C:\(...)\Projects\Dane. Aha! By=bym zapomnia= - musimy zamkn/@ okno BDE Configuration
potwierdzaj/c (Yes) pytanie o zapis nowych ustawieA!
4. Zapiszmy nasz projekt w katalogu zad641. Mo1emy teraz wpisa@ we w=asno@
DatabaseName obiektu Table cie1k do danych, lub jeli utworzylimy nowy alias - jego nazw
(pojawi si automatycznie na licie, jeli zrestartujemy C++ Buildera). W=asno@ TableName
ustawmy na TKlienci".
5. Zajmiemy si komponentem DataSourcel. W jego w=asno@ Name wpisujemy ^Klienci", a
DataSet ustawiamy na TKlienci.
6. Potrzebny nam bdzie jeszcze komponent DBGrid z karty Data Controls, za pomoc/ ktrego
mo1na przegl/da@ i modyfikowa@ dane w tabelach. Jego nazw (Name) ustawiamy na
TGKlienci", a Erd=o danych (DataSource) na SKlienci".
7. Mo1emy teraz uaktywni@ tabel (w=asno@ TKlienci->Active ustawiamy na true).
8. Aby uzyska@ dok=adn/ kontrol wprowadzanych danych, bdzie nam potrzebna statyczna lista pl
tabeli. Klikamy podwjnie w obiekt TKlienci. Pojawi si edytor pl (Fields editor). Klikamy
prawym klawiszem myszy, aby otworzy@ menu kontekstowe i wybieramy Add Fields, a nastpnie
zatwierdzamy list przez OK.
Rysunek 6.9. Nowy alias w BDE Configuration
9. Utworzylimy statyczn/ list obiektw klasy TField odpowiadaj/cych kolejnym polom tabeli.
Oznacza to, 1e mo1emy si teraz odwo=ywa@ do pl tabeli poprzez te obiekty. Na przyk=ad jeli
napiszemy w kodzie programu TKlienciNAZWISKO ->AsString", bdzie to oznacza@ aktualn/
zawarto@ tekstow/ pola NAZWISKO w tabeli TKlienci. Musimy te1 pamita@ o tym, 1e jeli
zmienimy struktur tabeli, nie spowoduje to automatycznej zmiany zestawu obiektw TField.
Musielibymy zatem sami odpowiednio skorygowa@ list.
10. Mo1emy te1 edytowa@ w=asnoci tych p\ za pomoc/ Inspektora Obiektw tak ( samo, jak
czynilimy to w wypadku innych komponentw. Wystarczy w Edytorze pl zaznaczy@
odpowiedni/ pozycj, aby w Inspektorze Obiektw zobaczy@ odpowiadaj/ce jej w=asnoci i
zdarzenia.
11. I to w=anie interesuje nas najbardziej. Zaczniemy od odpowiednik masek wprowadzania. W polu
TKlienciNAZWISKO we w=asnoci EditMask wprowadzamy >L<>lllllllllllllllllllllllllllll;l; "
(trzydzieci znakw pocz/wszy od du1ej litery). W polu TKliencilMIE nanosimy EditMask
>L<>llllllllllllllllll;l; ".
12. W odniesieniu do pola ID_KLIENTA zastosujemy nieco inn/ metod - zatwierdzimy lub
odrzucimy ca=o@ danych wpisanych przez u1ytkownika. Wykorzystamy do tego celu zdarzenie
OnValidate. Wpiszemy w nim funkcj pilnuj/c/", aby nie zosta= wykorzystany zastrze1ony
zakres ID:
void _fastcall TForml::TK1ienciID_KLIENTAVal idateUField *Sender)
{
// jesli nr ID jest wiekszy niz 99999...
if (Sender->AsInteger>99999)
// ... wywolujemy wyjatek z odpowiednim komunikatem
throw Exception("ID powyzej 99999 zarezerwowane!");
}
Podsumowanie: w zale1noci od rodzaju pola (znakowe czy numeryczne) mamy do czynienia z
w=asnociami EditMask lub EditFormat. Wdzia=aniu s/ one podobne do w=asnoci znanego nam ju1
komponentu MaskEdit (zachcam do obejrzenia dokumentacji on linF"). Na uwag zas=uguje tak1e
w=asno@ DisplayFormat formatuj/ca wywietlanie danych. Zdarzenie OnValidate pozwala nam na
ca=ociowe przyjcie lub odrzucenie danych (poprzez wywo=anie wyj/tku).
Inne zdarzenia zwi/zane z polami statycznymi to: OnChange - zachodzi zaraz po zmianie
zawartoci w buforze rekordu;
OnGetText - przy ka1dym odwo=aniu do w=asnoci Text. Mo1na je wykorzysta@ do wywietlenia
czego innego ni1 w rzeczywistoci zawiera pole;
OnSetText - wywo=ywane, gdy w=asno@ Text otrzymuje now/ zawarto@.
Warto zauwa1y@ mo1liwo@ =/czenia tabel przy u1yciu edytora pl. Przyk=ad znajduje si w katalogu
EXAMPLES\DBTASKS\LOOKUP.
wiczenie 6.5. Wyszukiwanie danych
Wprowadzenie: w miar u1ytkowania bazy danych powikszaj/ swoj/ objto@ Z czasem
kluczowym problemem staje si szybkie dotarcie do danych. Jeli znalezienie danych w naszym
programie trwa=oby d=u1ej, ni1 znalezienie wpisu w papierowej kartotece - nie mamy co liczy@ na
sukces...
Borland Data Engine wchodz/cy w sk=ad pakietu C++ Builder posiada szereg mechanizmw
u=atwiaj/cych i przyspieszaj/cych dostp do danych. W poni1szym zadaniu wykorzystamy jeden z
nich, s=u1/cy do udostpnienia u1ytkownikowi mo1liwoci wyszukania rekordw wed=ug dowolnego
klucza.
Zadanie 6.5.1: zbudowa@ program wyszukuj/cy dane w tabeli Klienci.dbf.
Wykonanie:
1. Otwieramy projekt z zdania 6.4.1 i zapisujemy w folderze zad651 (wszystkie modu=y!).
2. Poniewa1 nasz program ma by@ elastyczny, udostpnimy mo1liwo@ wyszukiwania danych wed=ug
dowolnego klucza. Bdzie nam do tego potrzebnych kilka nowych indeksw. Metod/ z zadania
6.3.3 p. 3-6 dodajemy do tabeli Klienci.dbf indeksy wed(ug pl NAZWISKO i IMI^.
3. Teraz czas na komponent RadioGroup, za pomoc/ ktrego udostpnimy u1ytkownikowi
mo1liwo@ wybierania aktywnego indeksu (porz/dku). W=asno@ Cap-

tion obiektu RadioGroupl ustawiamy na porzSdek danych wed7ug:". We w=asnoci Items (TStrings)
wpisujemy kolejne linie: Jdentyfikatora Klienta", ,flazwiska", Jmienia". Na kliknicie (OnClick)
odpowiemy:
void __fastcall TForml::RadioGrouplClickCTObject *Sender)
{
switch (RadioGroupl->ItemIndex) // ktry RadioButton jest
zaznaczony?
{
// jezeli pierwszy RadioButton - porzadek wedlug ID klienta
case 0:TKlienci->IndexName="ID_KLIENTA";break;
// jezeli drugi RadioButton - porzadek wedlug nazwiska
case l:TKlienci->IndexName="NAZWISKO";break;
// jezeli trzeci RadioButton - porzadek wedlug imienia
case 2:TKlienci->IndexName="IMIE";break;
}
}
4. Nastpna sprawa to pole edycji, w ktre u1ytkownik wpisze szukan/ wartos'@. Wykorzystamy do
tego celu zwyk=y Edit. Ustawiamy w=asno@ Text na " (usuwamy zawarto@ - nie chcemy, aby
u1ytkownik zosta= zmylony jego tekstem). W=asno@ OnChange bdzie wskazywa@ na funkcj
wyszukuj/c/ tekst zawarty w Editl->Text po aktualnym indeksie:
void __fastcall TForml::EditlChangeCTObject *Sender)
{
if (TK1ienci->IndexName"ID_KLIENTA") // jesli pole numeryczne
{ // FindNearest wymaga parametru typu TVarRec
TVarRec q(_atold(Editl->Text.c_str()));
// c_str() - kolejna metoda na zmiana formatu tekstu
// za pomoca funkcji FindNearest szukamy wartosci najblizszej temu
// co uzytkownik wprowadzil w Editl
TK1ienci->FindNearest(&q ,0);
}// do dokladnego wyszukania uzylibysmy FindKey
else // reszta pl jest typu znakowego
{ // AnsiString - TVarRec
AnsiString s=Editl->Text;
TVarRec q(s);
TK1ienci->FindNearest(&q ,0); //wyszukujemy zblizony ciag znakw
}
}
5. Aby wszystko by=o dla u1ytkownika jasne, Editl mo1emy opatrzy@ etykiet/ Tu wpisz szukan/
warto@:".
Podsumowanie: wyszukiwanie danych w ten sposb w naszej bazie, ktra ma 3 rekordy mo1e si
wyda@ mieszne. Za=o1 si jednak, 1e wcale nie wyda si ono mieszne komu, kto ma obs=ugiwa@
baz z setkami czy tysi/cami rekordw. Stwierdzi on wrcz, 1e jest to nieodzowne. Zastanwcie si -
c1 nam po nawet najbardziej cennych danych, jeli nie potrafimy do nich dotrze@? Zorganizowanie
u1ytkownikowi maksymalnie wygodnego i szybkiego dotarcia do danych, ktre go interesuj/, to po-
=owa sukcesu w pisaniu dobrych aplikacji.

Omwilimy tutaj tylko jedn/ metod wyszukiwania danych. C++ Builder dysponuje w tym zakresie
ca=ym arsena=em funkcji (g=wnie zwi/zanych z obiektami TTable i TDataSet). Je1eli interesuje nas
dok=adne wyszukiwanie i informacja o tym, czy znaleziono rekord - u1yjemy metody FindKey
(parametry identyczne jak w Find-Nearest). Przy kompleksowych (sk=adaj/cych si z wielu pl)
indeksach wygodniej jest skorzysta@ z metod SetKey i GotoKey. Je1eli nie chcemy u1ywa@ zbyt
wielu indeksw, mo1emy skorzysta@ z metody Locate. Zachcam czytelnikw do pogrzebania w
materia=ach przyk=adowych znajduj/cych si w katalogu Examples\DBTasks.
wiczenie 6.6. J%zyk zapytaD SQL
Wprowadzenie: jzyk SQL (Structured Query Language) zosta= stworzony w 1970 roku w
laboratoriach IBM. SQL jest jzykiem wysokiego poziomu. Aplikacja przesy=a zapytanie i czeka na
odpowiedE. Obci/1enie zwi/zane z koniecznymi obliczeniami, wyszukiwaniem itp. spada na system
zarz/dzania baz/ danych.
W C++ Builderze mamy do dyspozycji komponent TQuery, ktry s=u1y do zadawania pytaA i
wydawania poleceA w jzyku SQL. Mo1emy przy jego u1yciu sumowa@, grupowa@, filtrowa@ dane, a
tak1e wykonywa@ na nich wiele skomplikowanych operacji. Komponenty TTable i TQuery s/
potomkami tej samej klasy reprezentuj/cej zestaw rekordw (TDataSet). Jednak TQuery realizuje
swe zadania w odmienny sposb -aby ujrze@ zestaw rekordw, musimy zada@ odpowiednie pytanie w
jzyku SQL.
Zapytanie w jzyku SQL jest 1/daniem zwrcenia zestawu rekordw, ktre spe=niaj/ okrelone w nim
kryteria. Bardzo =atwo (za pomoc/ jednego polecenia) mamy mo1liwo@ otrzyma@ sumy
poszczeglnych pl, czy te1 zestaw rekordw bd/cy po=/czeniem kilku tabel. Zapytanie wybieraj/ce
(zwracaj/ce zestaw rekordw) tworzymy przy u1yciu instrukcji SELECT. Istniej/ tak1e instrukcje
s=u1/ce do pobierania informacji, wstawiania, usuwania i modyfikacji rekordw.
Wnajprostszym przypadku mo1emy za1/da@ zwrotu ca=ej tabeli:
SELECT * FROM Klienci
Symbol * oznacza i1 pobierane maj/ by@ wszystkie pola. Klauzula FROM wskazuje natomiast, o
ktr/ tabel chodzi (w naszym wypadku jest to tabela Klienci). Taka forma zapytania jest w=aciwie
odpowiednikiem obiektu TTable.
Jeli interesuje nas jedynie pewien wycinek tabeli, mo1emy zredukowa@ zarwno list wywietlanych
pl, jak i liczb interesuj/cych nas rekordw.
Wpierwszym przypadku musimy wpisa@ po s=owie SELECT nazwy pl, ktre nas interesuj/:
SELECT Nazwisko, Imie FROM Klienci
Otrzymamy dane ograniczone jedynie do tych dwch pl. Jest to prostsze ni1 korzystanie z
komponentu TTable i zwi/zanego z nim edytora pl.
W poni1szym zadaniu postaramy si zbudowa@ program, ktry umo1liwi nam testowanie r1nych
pytaA SQL na zestawie naszych kilku tabel przyk=adowych.

Zadanie 6.6.1: wykorzystuj/c komponent TQuery zbudowa@ aplikacj wykonuj/c/ zapytania SQL
wpisywane przez u1ytkownika.
Wykonanie:
1. Z menu File wybieramy New Application. Ustawiamy w=asno@ Forml->Caption na Test SQL".
2. Z karty Data Access pobieramy komponent Query. Nadajemy mu nazw (Name) TSQL".
W=asno@ DatabaseName ustawiamy na cie1k do danych lub nazw aliasu (CBTEST).
3. Z tej samej karty (Data Access) pobieramy DataSource nadaj/c mu nazw SSQL", a w=asno@
DataSet - TSQL.
4. Komponent DBGrid z karty Data Controls umieszczamy w dolnej czci formularza nazywaj/c
go TGSQL". Pod=/czamy go do Erd=a danych, ustawiaj/c jego w=asno@ DataSource na SSQL.
5. Na karcie Standard znajdziemy obiekt Memo, ktry umiecimy w grnej czci formularza.
Nazwiemy go ,JASQU\ Poniewa1 nie chcemy, aby wywietla= po uruchomieniu programu mieci -
czycimy w=asno@ Lines.
6. Z tej samej karty pobieramy przycisk (Button) lokuj/c go w rodku formularza i nazywaj/c
,BSQL". Jeszcze odpowiedni napis (Caption) informuj/cy u1ytkownika o funkcji przycisku -
Wykonaj SQL".
7. Strona operacyjna naszej aplikacji bdzie bardzo prosta - w=aciwie tylko reakcja na kliknicie w
przycisk - we w=asnoci OnClick umieszczamy funkcj:
void __fastcall TForml::BSQLC1ick(TObject *Sender)
{
// jesli stan = nieaktywne - dezaktywizujemy
if (!(TSQL->State==dsInactive)) TSQL->Active=false;
TSQL->SQL->C1ear(); // czyscimy poprzednia zawartosc
TSQL->SQL->AddStrings(MSQL->Lines); // dodajemy zawartosc Memo
TSQL->Active=true;// uaktywniamy zapytanie
}
Podsumowanie: mo1emy teraz w=asnorcznie sprawdzi@ w dzia=aniu instrukcj SELECT. Wpiszmy
przyk=ady z wprowadzenia. Czy dzia=a? Jeszcze jak!
Przy zadawaniu pytaA wolno nam korzysta@ z operatorw arytmetycznych. Mamy te1 mo1liwo@
u1ycia funkcji UPPER (du1e litery), LOWER (ma=e litery), SUBSTRING (podci/g znakw) i TRIM
(obetnij). Funkcja EXTRACT operuj/ca na danych zwi/zanych z dat/ i czasem umo1liwia uzyskanie
interesuj/cych szczeg=w. Na przyk=ad zapytanie
SELECT Data_Zamow, EXTRACT( DAY FROM Data_Zamow) FROM
Zamw
spowoduje pojawienie si odpowiedzi zawieraj/cej dok=adne daty zamwieA, a w kolejnej kolumnie
odpowiadaj/ce im dni miesi/ca (mo1emy przetestowa@ to za pomoc/ wykonanego przed chwil/
programu).

Kolejna klauzula, WHERE, umo1liwia wybieranie rekordw za pomoc/ wyra1eA logicznych. Jeli
interesuj/ nas tylko produkty zamwione w wikszej ni1 1 liczbie sztuk, wpisujemy:
SELECT * FROM ZamowSc WHERE Ilosc>l
Dla porwnania to samo zapytanie mo1emy rwnie1 wpisa@ bez klauzuli WHERE. Pojawi=y si dwa
ukryte wczeniej rekordy z polem ILOSC = 1!
W klauzuli WHERE mamy te1 mo1liwo@ tworzenia kompleksowych wyra1eA przy u1yciu
operatorw logicznych takich jak AND (i) czy OR (lub).
SELECT * FROM ZamowSc WHERE Ilosc>l AND Ilosc<10
Z(Eczenia
Z=/czenia umo1liwiaj/ nam logiczne powi/zanie wielu tabel w jedn/ ca=o@. Struktura naszych
przyk=adowych tabel jest z punktu widzenia teorii baz danych najefektywniejsza. My jednak
chcielibymy te dane ogl/da@ jako jedn/ ca=o@. Zacznijmy od z=/czenia tabel Klienci i Zamwienia:
SELECT Klienci.*, Zamw.Data_Zamow FROM Klienci, Zamw WHERE
KIienci.Id_Klienta = Zamw.Id_Klienta
Gdy pytanie dotyczy wielu tabel, przed nazw/ pola umieszczamy nazw tabeli, ktra je zawiera
(zauwa1my, 1e nie zmienia to dzia=ania symbolu *). W klauzuli FROM wymieniamy nazwy tabel
u1ytych w z=/czeniu, a w klauzuli WHERE - warunek =/cz/cy (w naszym wypadku =/czymy wed=ug
pl ID_KLIENTA obu tabel). W ka1dej chwili mo1emy do=/czy@ kolejn/ tabel (ZamowSc)
modyfikuj/c zapytanie:
SELECT Klienci.*, Zamw. Data_Zamow, ZamowSc* FROM Klienci,
Zamw, ZamowSc WHERE KIienci.Id_Klienta = Zamw.Id_Klienta AND
Zamw.Id_Zamow - ZamowSc.Id_Zamow
Do wstpnej analizy danych mo1emy u1y@ Klauzuli GROUP BY. Jeli interesuj/ nas liczby
produktw w poszczeglnych zamwieniach, wpisujemy:
SELECT Id_Zamow, SUM( Ilosc) FBOM ZamowSc GROUP BY
Id_Zamow
Mo1emy te1 wy=uska@ podobn/ informacj dotycz/c/ klientw poprzez jednoczesne zastosowanie
z=/czenia i klauzuli GROUP BY:
SELECT Klienci.Id_Klienta, SUM( ZamowSc.Ilosc) FROM Klienci,
Zamw, ZamowSc WHERE KIienci.Id_Klienta - Zamw.Id_Klienta AND
Zamw.Id_Zamow = ZamowSc.Id_Zamow GROUP BY KIienci.Id_Klienta
Funkcja SUM() oblicza sum kolejnych grupowanych rekordw. Jest to jedna z tzw. funkcji
agreguj/cych. Prcz niej mamy te1 do dyspozycji:
AVG() - rednia warto@ pola dla zbioru rekordw MIN() - minimalna warto@ pola dla zbioru
rekordw MAX() - warto@ maksymalna COUNT() - liczba rekordw

Nastpna bardzo przydatna klauzula to ORDER BY. Umo1liwia ona zdefiniowanie porz/dku, w
ktrym beda posortowane rekordy stanowiqce odpowiedz na pytanie. Wystarczy w niej podac nazwe
pola, wedlug ktorego chcemy uporzqdkowac dane. Jesli chcemy otrzymac zawartosc tabeli Klienci w
porzadku alfabetycznym wedlug nazwiska, wpisujemy:
SELECT * FROM Klienci ORDER BY Nazwisko
Mozliwosci jezyka SQL s^ duzo wieksze. Zachecam do przejrzenia plikow pomocy dostepnych w
menu Start->Programy->BorIand C++ Builder ->Help->Local SQL Help, a takze plikow
tutorial.hlp i WiSQL32.hlp dostepnych w katalogu INTERBASE (domyslnie C:\ Program
Files\Borland\IntrBase\Bin).
Cwiczenie 6.7. INTERBASE - srodowisko typu klientlserwer
Wprowadzenie: InterBase to system zarz/dzania relacyjnymi bazami danych (RDBMS)
przeznaczony dla srodowisk zarowno wielostanowiskowych, jak i jedno-stanowiskowych. Jest on
oparty na wieloplatformowej technologii klient/server. Moze pracowac pod Windows 95, Windows
NT, Novell NetWare, a takze w wielu imple-mentacjach systemu operacyjnego UNIX.
Do wersji Professional i Client/Server pakietu C++ Builder dolaczony jest Borland InterBase
Server bedacy lokalna wersjq serwera baz danych Borland Workgroup Server. Jego technologia
klient/serwer pozwala na jednoczesn^ prace wielu uzytkow-nikow. Lokalny serwer InterBase
pozwala na symulacje takiej pracy na pojedynczym komputerze.
Wpakiecie C++ Builder (niestety tylko w wersjach Professional i Client/Server) znajduj/ sie specjalne
narzedzia zarzadzajace prac/ z InterBase. Jest to InterBase Interactive SQL (Wisql32.exe)
wspomagajacy interaktywn/ prace z baz/ danych w jezyku SQL oraz InterBase Server Manager
(Ibmgr32.exe) organizuja.cy administrowanie bazami (rejestracja uzytkownikow, kopie
bezpieczefistwa itp.).
Zadanie 6.7.1: za pomocy InterBase Interactive SQL zalozyc now/ baze danych o nazwie VIDEO.
1. Uruchamiamy WISQL32 (Interactive SQL). Wybieramy File->Create Database.
2. W okienku dialogowym Create Database wpisujemy nazwe bazy wraz z pelna^ sciez-kq
dostepu((..)\VTDEO.GDB) oraz identyfikator i hasto uzytkownika (np. SYSDBA i masterkey).
Naciskamy OK. InterBase stworzy nowa^ baze danych, go ktorej zosta-niemy automatycznie
podlaczeni.
3. Mozliwosci jezyka SQL nie koAcz/ sie na zapytaniach wybierajacych (SELECT). Mozemy
rowniez z jego pomoc/ tworzyc nowe tabele. Sluzy do tego instrukcja CREATE TABLE. W
naszej nowej bazie danych utworzmy tabele KASETY (wpisujemy w pole SQL Statement):

Rysunek 6.10.
CREATE TABLE Kasety ( // nazwa tabeli - Kasety
NrKasety INTEGER, // pole NrKasety typu ca=kowitego
Tytul CHARC20), // Tytul - pole o d=ugosci 20 znakow
Rodzaj CHAR(IO), // Rodzaj - pole typu znakowego
IlWypoz INTEGER ) .11 IlWypoz - liczba wypozyczen
4. Tworzac powyzszq tabele popelnilismy pewien Mad - nie zdefiniowalismy kiucza gtownego.
Klucz glowny definiuje Sie w SQL za pomoc/ klauzuli PRIMARY KEY. Wida@ wyraEnie, ze w
naszej tabeli na klucz glowny wietnie nadaje sie pole NrKasety. Usuniemy teraz niezbyt
poprawn/ tabele u1ywaj/c DROP TABLE:
DROP TABLE Kasety
5. Tworzymy od nowa tabele Kasety, tym razem definiuj/c jej klucz glowny (mo1emy nacisn/@ dwa
razy Previous i nanie@ jedynie poprawki):
CREATE TABLE Kasety (
NrKasety INTEGER NOT NULL PRIMARY KEY, //klucz glwny
Tytul CHAR(20),
Rodzaj CHARUO),
IlWypoz INTEGER )
Napis NOT NULL oznacza, ze pole nie moze byc puste.
7. Nasza tabela jest pusta. Aby wprowadzi@ do niej rekordy uszyjemy instrukcji INSERT:

INSERT INTO Kasety(NrKasety, Tytul, Rodzaj, IlWypoz) VALUES
("1", "Wielkie zarcie", "Groteska", "0")
Jak wida@, w klauzuli INTO poza nazw/ tabeli podajemy w nawiasach nazwy pl, do ktrych
wprowadzamy dane. W klauzuli VALUES podajemy kolejno wartoci tych pl.
7. Nanosimy kolejne rekordy (mo1emy pos=u1y@ si przyciskiem Previous):
2", Czerwony Kapturek", Bajka", 5"
3", Banie Polskie", Bajka", 2"
4", Romeo i Julia", Klasyka", 0"
8. Jeli w ktrym miejscu pomylimy si, mo1emy u1y@ instrukcji UPDATE do poprawienia
rekordu. Na przyk=ad, jeli pomylilimy si co do liczby wypo1yczeA kasety Romeo i Julia",
mo1emy wykona@ nastpuj/c/ komend SQL:
UPDATE Kasety
SET IlWypoz="l" //ustawiamy pole IlWypoz na 1"
WHERE NrKasety="4"
W tym wypadku modyfikujemy jeden rekord. W warunku WHERE podalimy warto@ klucza
g=wnego tabeli, co jest najlepsz/ gwarancj/, 1e modyfikacja nie obejmie 1adnego innego wiersza.
9. Jeli stwierdzimy, 1e pewien rekord do niczego si ju1 nie nadaje (z powodu b=dw, lub dlatego,
1e diabli wzili kaset...), mo1emy bezbolenie usun/@ go za pomoc/ instrukcji DELETE:
DELETE FROM Kasety
WHERE NrKasety="4" // 4" to Romeo i Julia"
Uwaga! Pominicie klauzuli WHERE oznacza@ bdzie usunicie wszystkich rekordw tabeli!
Podsumowanie: tak oto za=o1ylimy pod InterBase now/ baz danych na temat kaset VIDEO,
otrzymuj/c przy okazji pokaEn/ dawk wiadomoci na temat jzyka SQL.
Pod C++ Builderem wsp=prac z InterBase organizuj/ komponenty TDatabase i
TStoredProcedure. Poniewa1 Borland nie do=/cza serwera InterBase do wszystkich wersji swego
pakietu (brak go w wersji Standard), nie bdziemy zajmowa@ si tutaj wsp=prac/ C++ Buildera z
InterBase. Wszystkich tych ktrzy s/ szczliwymi posiadaczami odpowiednich wersji, odsy=am do
dokumentacji on lin" firmy Borland.
Wnastpnym (ostatnim) rozdziale zajmiemy si innymi interesuj/cymi mo1liwociami pakietu, takimi
jak budowa plikw pomocy Windows (HLP), dynamiczna wymiana danych (DDE), a tak1e obs=uga
archiww (LZ).

7. Pliki pomocy Windows, DDE, modu( LZExpand
wiczenie 7.1. Projekt prostego pliku pomocy Windows
Wprowadzenie: wraz z pakietem C++ Builder dostarczane s/ narzdzia usprawniaj/ce budowanie
plikw pomocy (popularnych HLP-w).
Wpodkatalogu C++ Buildera HELPYTOOLS znajdziemy Microsoft Help Workshop
-narzdzie znakomicie u=atwiaj/ce utworzenie i kompilacj pliku pomocy.
Wytworzenie pe=nowartociowego pliku pomocy sk=ada si z trzech etapw:
zapisanie w=aciwej treci w pliku o formacie Rich Text Format (RTF)
utworzenie pliku projektu (HPJ), spisu treci (CNT)
powi/zanie kolejnych tematw pomocy z kontekstem (pomoc kontekstowa) i kompilacja do
ostatecznej postaci (HLP)
Wponi1szych zadaniach przejdziemy przez wszystkie te trzy etapy.
Zadanie 7.1.1: utworzy@ plik RTF.
Wykonanie:
1. Bdzie nam potrzebny Microsoft Word lub inny edytor tekstu obs=uguj/cy format RTF (Rich Text
Format). W naszym przyk=adzie opiszemy sposb postpowania pod edytorem Word 7.0a.
2. Z menu Plik wybieramy pozycj Nowy, a dalej z karty Oglne - Pusty dokument. Zapiszemy
teraz nasz dokument jako Htest.rtf w folderze Projects\zad71. Wybieramy Plik->Zapisz jako.
Pojawi si okienko zapisu. W pozycji Zapisz jako typ ustawiamy Tekst formatowany RTF, w
pozycji Nazwa pliku wpisujemy ,JHtest.rtf i wybieramy odpowiedni katalog (zad71 - jeli nie istnieje
- utworzy@). Wybieramy Zapisz - nasz dokument posiada ju1 teraz format RTF.
3. Utworzymy teraz dwa przyk=adowe tematy pomocy (ang. topie). Wpisujemy:
Temat pierwszy
Tu tekst pierwszego tematu
znak koca strony (wybieramy Wstaw ->Znak podz1au-> Strony)
Temat drugi
Tu tekst drugiego tematu i odnosnik do tematu pierwszego
znak koca strony

4. Ustawiamy kursor na pocz/tku tekstu Temat pierwszy". Wybieramy Wstaw-> Przypisy. Uka1e
si okienko Przypisy i przypisy ko)cowe. W grupie Numerowanie wybieramy Znak
niestandardowy i wpisujemy #. Naciskamy OK i w polu przypisu dopisujemy IDH_Temat
pierwszy (identyfikator tematu). Postpuj/c podobnie dodajemy nastpne przypisy:
Znak niestandardowy tekst przypisu znaczenie
$ Temat pierwszy tytu=
K Temat pierwszy pozycja w indeksie
Identycznie postpujemy z tematem drugim.
5. Za=o1ymy jeszcze odnonik do tematu pierwszego w temacie drugim. Ustawiamy kursor zaraz za
koAcem tekstu tematu drugiego. Wpisujemy identyfikator tematu pierwszego (,JDH Temat
pierwszy")
6. Zaznaczamy teraz tekst odnonika (odno.nik do tematu pierwszego). W razie problemw z
mysz/ mo1emy u1y@ klawiszy Shift+ strza(ka. Wybieramy Format ->Czcionka->Podkre.Ienie-
>Podwjne. Zaznaczamy obok identyfikator tematu docelowego (IDH_Temat pierwszy) i
wybieramy Format->Czcionka->Ukrycie.
7. Nasz plik jest gotowy. Mo1emy teraz ponownie zapisa@ go na dysku (Zapisz).
Zadanie 7.1.2: utworzy@ plik HLP.
Wykonanie:
1. Do utworzenia pliku projektu (HPJ) i kompilacji pliku HLP pos=u1y nam Microsoft Help
Workshop znajduj/cy si w podkatalogu C++ Buildera ((...)\HELP\TOOLS\HCRTF.EXE).
2. Po jego uruchomieniu wybieramy File->New->Help Project. Wpisujemy Htest" (Htest.hpj) i
zapisujemy w katalogu Projects\zad71.
3. Na ekranie pojawi si projekt pomocy (Htest.hpj). Z prawej widoczne s/ g=wne dostpne opcje.
Wska1emy teraz plik z treci/ pomocy. Wybieramy Files->Add, a nastpnie odnajdujemy
utworzony w poprzednim zadaniu plik Htest.rtf.
4. Mo1emy kompilowa@! S=u1y do tego widoczny u do=u przycisk Save and Com-pile.
5. Je1eli otworzymy spod Explorera Windows nasz plik pomocy, stwierdzimy na pewno, 1e jest on
bardzo ubogi. Dodamy do niego spis treci.
6. W programie Microsoft Help Workshop wybieramy FiIe->New->Help Con-tents. W pozycji
Default filename wpisujemy ,Htest", a w Default title - Test pliku HLP".
7. Teraz dodamy zawarto@ spisu. Wybieramy Add Below. Pojawi si okienko Edit Contents Tab
Entry. Wpisujemy w pozycji Title - Tytu7 tematu pierwszego", w pozycji Topie ID
IDH_Temat pierwszy", reszt pl mo1emy zostawi@ bez wype=nienia.
8. Podobnie postpujemy z tematem drugim.
9. Zapisujemy nasz spis treci wybieraj/c File->Save As. Wpisujemy ,JItest.cnt".
10. Przechodzimy do pliku projektu (Window->Htest.hpj) i naciskamy Save and Compile.
11. Je1eli otworzymy teraz spod Explorera plik Htest.hlp, zauwa1ymy, 1e pojawi= si spis treci.
Zadanie 7.1.3: utworzy@ pomoc kontekstow/.
Wykonanie:
1. C++ Builder umo1liwia nam wykorzystanie pomocy kontekstowej. Wszystkie komponenty
biblioteki VCL posiadaj/ w=aciwo@ HelpContext, ktra jest liczb/ ca=kowit/. Musimy
zdefiniowa@ numeryczne wartoci dla tematw, ktre mamy w pliku HLP, tak aby mo1na by=o je
umieci@ we w=asnociach HelpContext odpowiednich obiektw.
2. W Microsoft Help Workshop otwieramy Htest.hpj. Wybieramy Map->Add i tu
wpisujemy w polu Topie ID IDHI_Temat pierwszy" i poni1ej w polu Mapped
numeric value wpisujemy 1".
Podobnie postpujemy z tematem drugim (,IDH_Temat drugi" i 2").
3. Plik pomocy jest przygotowany pod wykorzystanie kontekstowe. Uruchamiamy kompilacj (Save
and Compile).
4. Przechodzimy do C++ Buildera i otwieramy nowy projekt (File->New Application). Wybieramy
Options->Project. W karcie Application w pozycji Help file (plik pomocy) naciskamy Browse
(przegl/daj) i wybieramy Projects\zad71\ Htest.hlp lub wpisujemy po prostu Htest.hlp"
naciskaj/c OK.
5. Teraz mo1emy doda@ do formularza kilka obiektw, ktre po=/czymy z kontekstem pomocy.
Pobieramy komponent Edit i ustawiamy jego w=asno@ HelpCon-text na 1. Je1eli u1ytkownik w
polu edycji (Edit) nacinie klawisz FI, pojawi si zwi/zany z nim kontekst pomocy (czyli nasz
temat pierwszy w pliku Htest.hlp).
6. Wstawiamy jeszcze komponent Memo ustawiaj/c jego w=asno@ HelpContext na 2 i zapisujemy
projekt w katalogu zad71.
7. Mo1emy uruchamia@ aplikacj. W zale1noci od tego, na ktrym komponencie naciniemy FI,
pojawi si odpowiedni temat pomocy.
Podsumowanie: To o czym tu mwilimy, to jedynie wierzcho=ek gry lodowej. Takie zagadnienia,
jak dodawanie przyciskw, grafiki, animacji, pos=ugiwanie si wieloma plikami HLP - to wszystko
jest materia=em na kolejn/ ksi/1k. Pozostaje nam zajrze@ do plikw pomocy na temat plikw
pomocy. W katalogu HELPYTOOLS jest plik hcw.hlp, a w nim wiele dodatkowych informacji na
temat tworzenia pomocy dla Windows.

wiczenie 7.2. Dynamiczna wymiana danych (DDE) - komponent TWordDDE
Wprowadzenie: Dynamie Data Exchange (DDE) to jedna z form komunikacji midzy procesami
(programami), ktra u1ywa dzielonej (wsplnej) pamici dla wymiany danych miedzy aplikacjami.
Aplikacje te mog/ u1ywa@ DDE do jednorazowej wymiany danych lub te1 do udostpnienia czci
swych opcji innym procesom. W po=/czeniu typu DDE wyr1niamy dwie strony - DDE Client i DDE
Server. DDE Client jest zwykle stron/ nawi/zuj/c/ dialog z DDE Server.
W C++ Builderze mamy do dyspozycji kilka komponentw s=u1/cych do nawi/zania i utrzymania
po=/czenia DDE: DDEClientConv, DDECIientltem, DDEServerConv i DDEServerItem. W
poni1szych zadaniach omwimy cz@ z nich przy okazji tworzenia specjalizowanego komponentu
nawi/zuj/cego dialog z programem Microsoft Word.
Zadanie 7.2.1: utworzy@ komponent TWordDDE.
Wykonanie:
1. Zamykamy wszystkie dokumenty (File->CIose Ali). Z menu Component wybieramy pozycj
New. Pojawi si okienko Component Wizard. Wpozycji Class Name wpisujemy nazw nowego
komponentu - TWordDDE". W polu Ancestor type wybieramy klas bazow/ - TComponent.
Naciskamy OK - C++ Builder wytworzy dla nas szkielet komponentu.
2. Komponent nie jest oczywicie jeszcze gotowy. Zapiszmy jednak jego szkielet (File->Save As) w
katalogu Projects\Komp jako WordDDE.cpp".
3. Zaczniemy od deklaracji klasy w pliku nag=wkowym (WordDDE.h), ktra po naszej ingerencji
powinna wygl/da@ mniej wicej tak:
#include <vcl\ddeman.hpp>
//.
class TWordDDE: public TComponent
{
private:
TFileName FExe;
TDdeClientConv *FDDE;
bool FPolaczony;
bool FDziala;
TNot1fyEvent FOnClose;
TNotifyEvent FOnOpen;
void __fastcall UstExe(const TFileName Sciezka);
void __fastcall UstPolaczonyCconst bool Wartosc);
bool __fastcall wezGotow(void);
protected:
void __fastcall OtwPolaczenieCTObject *Sender);
void __fastcall ZmkPolaczenieCTObject *Sender);
public:
__fastcall TWordDDECTComponent* Owner);
_fastcall TWordDDE::~TWordDDE();
void __fastcall Polacz(void) ;
void __fastcall Rozlacz(void);
char *Zadaj(char *0biekt);
void __fastcall Wykonaj(char *Komenda);
__property bool Polaczony = Cread=FPolaczony, write=UstPolaczony};
__property bool Gotw = Cread=wezGotow};
__published:
__property TFileName Exe = Cread=FExe, write=UstExe};
__property TNotifyEvent OnClose = Cread=FOnClose, write=FOnClose};
__property TNotifyEvent OnOpen = f.read=F0n0pen, write=F0n0pen};
};
Nie nale1y zapomnie@ o w=/czeniu (#include) pliku z deklaracj/ potrzebnej nam klasy
DDEClientConv (vcl\ddeman.h).
4. Przechodzimy teraz do modu=u g=wnego (WordDDE.cpp). Wpisujemy tu zawarto@ nastpuj/cych
metod:
__fastcall TWordDDE::TWordDDE(TComponent*Owner) // konstruktor
: TComponent(Owner)
{
// sprawdzamy czy dziala, czy tez faza projektowania
(csDesigning) FDziala=!(ComponentState.Contains(csDesigning));
if (FDziala)
{
// utworzenie obiektu odpowiedzialnego za DDE - DdeClientConv
FDDE = new TDdeClientConv(this);
// polaczenie "reczne"
FDDE->ConnectMode = ddeManual;
// odpowiednim zdarzeniom przypisujemy "nasze" funkcje
FDDE->0n0pen = OtwPolaczenie;
FDDE->OnClose = ZmkPolaczenie;
}
// domyslne Exe
UstExe("Winword");
}
__fastcall TWordDDE::~TWordDDE() // destruktor
{
// jesli program jest uruchomiony - niszczymy FDDE(DdeClientConv)
if (FDziala) FDDE->Free();
}
//
void __fastcall TWordDDE::UstExe(const TFileName Sciezka)
{
// zerwanie polaczenia
Rozlacz();
// ustawiamy Exe na parametr Sciezka
FExe=Sciezka;
if (FDziala) // jesli tryb uruchomienia
FDDE->ServiceApplication = FExe;
}
return FDDE->RequestData(Obiekt);
}
void _fastcall TWordDDE::Wykonaj(char *Komenda)
{
Application->ProcessMessages(); // przetwarzamy komunikaty
if (!FDDE->ExecuteMacro(Komenda, false)) // wykonujemy podana
komende,
{
while (FDDE->WaitStat) //czekamy na Winworda...
{
Application->ProcessMessages(); // przetwarzamy komunikaty
if (!FDDE->ExecuteMacro(Komenda,true)) // ponawiamy prbe
throw Exception("Komenda nie zaakceptowana przez Worda");
// cos nie tak - Winword odrzucil komende
}
}
}
5. To ju1 koniec - w naszej kolekcji mamy kolejny komponent. Zapiszmy dokonane
przez nas zmiany (FiIe->Save All). Mo1emy teraz zainstalowa@ komponent
TWordDDE w bibliotece VCL uruchamiaj/c Component->Install i wpisuj/c
WordDDE".
6. W razie k=opotw sprawdEmy, czy katalog $(BCB)\Projects\Komp znajduje si
na cie1ce przeszukiwaA kompilatora (Options->Environment->Library->Path -
patrz zadanie 3.1.1)
Zadanie 7.2.2: utworzy@ program testuj/cy dla komponentu TWord.
Wykonanie:
1. Utworzymy w pe=ni wartociowy program operuj/cy na edytorze Microsoft Word.
Do trwa=ego zapisu komend Visual Basic u1yjemy tabeli XBase WrdMacdbf. Mo1-
na j/ w=asnorcznie utworzy@ (co opisane jest poni1ej), ale mo1na tak1e pos=u1y@ si
gotow/ tabel/ z za=/czonej do ksi/1ki dyskietki (jest w niej zapisane kilkanacie goto-
wych komend Visual Basic).
2. Do w=asnorcznego utworzenia tabeli potrzebny nam bdzie Database Desktop.
(Uruchamiamy File->New->Table. Wybieramy dBASE for Windows i tworzymy
nastpuj/c/ struktur:
NAZWA" C" 40"
ZAWARTOpq" C" 250"
3. Przyda nam si indeks po polu NAZWA - Indexes->Define NAZWA". Zapisu-
jemy tabel jako WrdMacdbf (Save As) i zamykamy DBD.
4. Przejdziemy teraz do w=aciwej aplikacji (wybieramy File->New Application). Teraz
z karty Samples pobieramy komponent WordDDE i ustawiamy jego w=asno@ Exe na
cie1k do edytora (u mnie by=o to C:\MSOFFICE\WINWORD\Winword"). Za-
uwa1my, 1e niepotrzebne jest rozszerzenie exe".
5. Zgodnie z funkcj/ programu w nag=wek formularza (Caption) wpisujemy
Dynamiczna wymiana danych - WORD".
6. Pobieramy dwa obiekty Bevel (karta Additional - przedostatni) i lokujemy je
u gry i u do=u formularza.
7. Teraz z karty Data Access bierzemy komponent Table. W jego w=asno@ Name
wpisujemy TWrdMac", w TableName WrdMac.dbf a w DatabaseName -
Adam" lub alias CBTEST. Utworzymy teraz statyczn/ list pl tabeli. Poprzez
dwukrotne kliknicie w komponent TWrdMac wywo=ujemy Edytor Pl. Naci-
skamy prawy klawisz myszy i z kontekstowego menu wybieramy Add fields.
Zatwierdzamy utworzenie obu pl naciskaj/c OK.
8. Nastpnym potrzebnym nam komponentem jest Erd=o danych (DataSource -
karta Data Access). W jego w=asno@ Name wpisujemy SWrdMac", a DataSet
ustawiamy na TWrdMac.
9. Z karty Standard pobieramy ComboBox. W Name wpisujemy CB", a Text
ustawiamy na pusty ("). Umieszczamy go na grnym Bevel i rozci/gamy, tak aby
siga= od jego prawej do lewej krawdzi. Nad nim mo1emy umieci@ etykiet
(Label) z napisem Opis komendy:".
10. Poni1ej wstawimy przycisk (Button) Wykonaj". Aby napis by= widoczny, ustaw-
my Font->Height na 30.
11. Teraz jeszcze dolny Bevel. Umiecimy na nim pole edycji (Edit) rozci/gaj/c je
odpowiednio. Zaopatrzymy je w etykiet Komenda:".
12. Poni1ej umiecimy przycisk Dopisz do tabeli".
13. Nadesz=a pora na stron operacyjn/ aplikacji.
14. W pliku nag=wkowym w sekcji public bdzie nam potrzebna deklaracja specjal-
nego znacznika edycji WcKlaw. W sekcji public formularza (TForm1) dopisu-
jemy:
bool WcKlaw; // znacznik wcisniecia klawisza
15. Podczas tworzenia si formularza (czyli uruchamiania aplikacji) musimy wykona@
par wstpnych czynnoci. W zdarzenie OnCreate formularza wstawiamy:
void __fastcall TForml::FormCreateCTObject *Sender)
{
WordDDEl->Polacz(); // nawiazujemy polaczenie
TWrdMac->Active=true; // otwieramy tabele
TWrdMac->IndexName="NAZWA"; // "porzadkujemy" wedlug nazwy
TWrdMac->First(); // przejscie do pierwszego wpisu (rekordu)
WcKlaw=false; // poczatkowa wartosc znacznika wcisniecia klawisza
while (!TWrdMac->Eof) // do konca tabeli
{
CB->Items->Add(TWrdMacNAZWA->AsString); // dodajemy element do CB
TWrdMac->Next(); // nastepny rekord
}
)
16. Przy zakoAczeniu aplikacji (a wic podczas zamykania formularza OnClose)
musimy w poprawny sposb roz=/czy@ si z edytorem Word:
void __fastcall TForml::FormClose(TObject *Sender,
TCloseAction &Action)
{
WordDDEl->Rozlacz();
}
17. Kiedy u1ytkownik wybierze w rozwijanym polu edycji (CB) jak/ komend, mu-
simy odnaleE@ jej tre@ w tabeli i wstawi@ j/ w pole edycji (Edit1). W CB-> On-
Change wpisujemy:
void __fastcall TForml::CBChange(TObject *Sender)
{
// jesli nie jest wcisniety zaden klawisz - uzytkownik nie
edytuje CB
if UWcKlaw)
{
AnsiString s=CB->Text;
TVarRec q(s):
if (TWrdMac->FindKey(&q,0))
Editl->Text=TWrdMacZAWARTOSC->AsString;
ellse
MessageBox(0,"Nie znaleziono makra!", "BLAD!!!!!", 0);
)
}
18. Z kolei nacinicie lub zwolnienie dowolnego klawisza ma powodowa@ odpo-
wiednie ustawienie znacznika WcKlaw. W zdarzenia OnKeyDown i OnKeyUp
komponentu CB wpisujemy wic:
void __fastcall TForml::CBKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
WcKlaw=true; // klawisz wcisniety
}
void __fastcall TForml::CBKeyUp(TObject *Sender, WORD &Key,
TShiftState Shift)
{
WcKlaw=false; // zwolniono klawisz
}
19. Je1eli u1ytkownik wciska przycisk Wykonaj" (Buttonl), ma zapewne ochot
przes=a@ do edytora Word komend, ktrej tre@ widzi w polu edycji Editl. Zda-
rzeniu OnClick przycisku Buttonl przypisujemy wic:
void __fastcall Tform1::ButtonlClick(T0bject *Sender)
{
char Bufor[2003; // bufor na tekst
strcpy(Bufor,"["); // "opakowujemy" komenda w klamry []
strcat(Bufor,Editl->Text.c_str()); // konwersja tekst - tekst
strcat(Bufor,"]"); // zamykamy klamra (dodajemy "]")
W WordDDE1->Wykonaj(Bufor); // wysy=amy komend/ do Worda
}
20. Pozosta=o nam jeszcze obs=u1enie nacinicia Button2 (Dopisz do tabeli"). Nie
omielilibymy si oszukiwa@ u1ytkownika, pos=usznie wklepujemy wic w zda-
rzenie OnClick:
void __fastcall TForml::Button2Click(TObject *Sender)
{
TWrdMac->Insert(); // wstawienie pustego rekordu
TWrdMacNAZWA->AsString=CB->Text; // pole NAZWA ustawiamy na opis w
CB
TWrdMacZAWARTOSC->AsString=Editl->Text; // pole ZAWARTOSC - Editl-
>Text
TWrdMac->Post(); // zatwierdzamy zmiany w rekordzie
CB->Items->Add(CB->Text); // dodajemy tez nowa pozycje do CB
}
21. Staro1ytni mawiali: Koniec wieAczy dzie=o". U nas zwieAczeniem operacji jest
zapis efektw w odpowiednim katalogu (zad722). Mo1emy teraz uruchomi@ nasz
program testuj/cy mo1liwoci DDE i (o ile posiadamy wietny sk/din/d edytor
Microsoftu) wy1y@ si wydaj/c zdalnie rozkazy naszemu nowemu niewolnikowi.
Rysunek 7.1. WordDDE w dzia=aniu
Podsumowanie: sposb dzia=ania tej aplikacji mo1na w=aciwie wyczyta@ z listingw.
Dla pewnoci jednak napisz kilka s=w o jej obs=udze. Je1eli u1ylicie gotowej tabeli
(tej z dyskietki), macie do dyspozycji zestaw gotowych komend. Wystarczy wybra@
w rozwijanym polu edycji odpowiedni opis komendy, aby w polu edycji poni1ej poja-
wi=a si jej tre@. Je1eli teraz naciniemy Wykonaj" - Word pos=usznie wykona to, co
mu rozka1emy. Dodatkowa mo1liwo@ tego programu to dopisywanie nowych ko-
mend (z przyjemnoci/ odsy=am do dokumentacji on lin" na temat Visual Basica

edytora Word). Aby dopisa@ now/ komend musimy wpisa@ jej tre@ w pole edycji, jej
opis w rozwijane pole edycji (po to w=anie potrzebowalimy znacznika WcKlaw -
aby zobojtni@" zdarzenie OnChange obiektu CB na zmiany nanoszone z klawiatury).
W razie k=opotw z kompilacj/, sprbujcie doda@ cie1k do komponentu WordDDE
wybieraj/c Options->Project->karta Directories/Conditionals i dopisuj/c do pozy-
cji Include Path i Library path cie1k $(BCB)|Projects|Komp. Swoj/ drog/ to
dziwne, jak opornie C++ Builder przyjmuje wszelkie zmiany w konfiguracji. Jeli to
nie pomo1e, mo1na sprbowa@ Project->Build All lub restartu systemu - u mnie da=o
to efekty.
Tak oto dotarlimy szczliwie do koAca owego intensywnego kursu programowania
Windows". Jeli nie wszystko zrozumielicie, nie przejmujcie si - sprbujcie jeszcze
raz. Jeli nie pomo1e, kolejny. Upr i nieco szczcia - oto czego Wam trzeba, aby
nauczy@ si pos=ugiwa@ dowolnym narzdziem. Zwykle co, co wydaje si strasznie
skomplikowane z zewn/trz - od rodka okazuje si zupe=nie proste...

You might also like