R06-03.DOC

(452 KB) Pobierz
Szablon dla tlumaczy

39

 

Rozdział 6.            Komponenty biblioteki VCL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C++Builder jest systemem służącym do szybkiego tworzenia aplikacji. Jest to możliwe między innymi dzięki istnieniu biblioteki komponentów wizualnych VCL (ang. Visual Component Library). Biblioteka VCL zawiera gotowe do użycia obiekty wizualne i niewizualne oraz klasy „opakowujące”, co niezwykle upraszcza tworzenie aplikacji. Naturalnie jeśli zachodzi taka potrzeba, C++Builder umożliwia tworzenie oprogramowania bez wykorzystania biblioteki VCL.

Głównym zadaniem C++Buildera jest tworzenie programów zorientowanych obiektowo. Poniższy rozdział jest jedynie wstępem do tego zagadnienia. C++Builder wykorzystuje koncepcję wielokrotnego użycia obiektów dzięki zastosowaniu komponentów. Tworzenie aplikacji jest wyjątkowo łatwe – wystarczy umieścić komponent na formularzu, zdefiniować jego właściwości i napisać program obsługujący zdarzenia.

W rozdziale tym przyjrzyjmy się, jak obiekty biblioteki VCL dziedziczą od klasy TObject. Zobaczymy także, jak inspektor obiektów umożliwia, dzięki użyciu mechanizmu RTTI, korzystanie z narzędzi do sprawdzania i edycji właściwości oraz zdarzeń wspólnych dla wielu komponentów. Dokonamy porównania typowych klas C++ i klas VCL, zajmiemy się też udoskonaleniami wprowadzonymi do języka, co pozwoli przedstawić udogodnienia, jakimi dysponuje programista, używający C++Buildera.

Zajmiemy się także standardowymi elementami sterującymi oraz tym, na jakie uwarunkowania występujące w bibliotece COMCTL32.DLL (będącej częścią systemu Windows) należy zwrócić uwagę. Przyjrzymy się też jej rozszerzeniom wprowadzonym w C++Builderze.

Następnie zapoznamy się z innymi znaczącymi udoskonaleniami oferowanymi przez C++Buildera 5 – można do nich zaliczyć między innymi nowe możliwości formatowania tekstów podpowiedzi i menu, dodatkowe funkcje dostępu do rejestru czy komponenty TApplicationEvents oraz TIcon.

W kolejnym kroku dokonamy rozbudowy biblioteki VCL, a ściślej rzecz biorąc klasy TStringList. W podrozdziale tym zademonstrujemy, jak łatwo można tworzyć aplikacje, opierając się na klasie kontenerowej TStringList, włączając w to przechowywanie w niej standardowych struktur i klas. Następnie krótko przedyskutujemy nowe zawansowane zdarzenia związane z obsługą niestandardowych funkcji rysowania (custom draw) dla komponentów: TTreeView, TListView i TToolBar. Dowiemy się także, jak szybko tworzyć aplety Panelu sterowania.

W końcu zajmiemy się komponentami innych firm, mogącymi przyspieszyć tworzenie aplikacji i stanowić podstawę dalszej rozbudowy już istniejących „otoczek” API. Komponenty te budowane są w oparciu o wchodzącą w skład C++Buildera bibliotekę VCL oraz model PME (ang. properties, methods, eventswłaściwości, metody, zdarzenia).

Wprowadzenie do biblioteki VCL

W dokumentacji C++Buildera można znaleźć schemat przedstawiający hierarchię

to nie do końca to samo, co struktura.

obiektów biblioteki VCL. Jak łatwo się domyślić, w każdej kolejnej wersji C++Buildera schemat ten staje się bardziej rozbudowany. Trzeba jednak pamiętać, że nie oddaje on całkowicie złożoności biblioteki VCL. Powód jest bardzo prosty – ta ostatnia nie składa się jedynie z obiektów pochodzących od swoich przodków, lecz oparta jest na modelu PME, obejmującym właściwości, metody i zdarzenia. Architektura ta łączy w sobie paletę komponentów, inspektora obiektów i zintegrowane środowisko programistyczne (IDE), tworząc doskonałe narzędzie, umożliwiające szybkie tworzenie aplikacji (ang. RAD – Rapid Application Development) dla systemu Windows. Do zbudowania programu wystarczy

tu brakowało początku zdania (zaczynało się od „Wystarczy...”).

umieszczenie na formularzu kilku komponentów. Oczywiście aplikacja nie mogłaby być w pełni funkcjonalna, gdyby programista wcale nie napisał kodu programu. Biblioteka VCL wykonuje jednak większość pracy za niego, czyniąc tworzenie oprogramowania bardziej praktycznym i przyjemnym. Dzięki temu nie trzeba tracić czasu na nieciekawe i monotonne tworzenie szkieletu aplikacji dla Windows, można zaś dokładniej zająć się zagadnieniami, które mają być realizowane przez program.

Pozostałą część podrozdziału poświęcimy na krótką analizę hierarchii obiektów VCL

Pierwotne tłumaczenie tego zdania odbiegało sensem od oryginału.

.

Klasa TObject – początek przygody

Biblioteka VCL składa się z grupy obiektów wywodzących się z abstrakcyjnej klasy TObject. Klasa ta umożliwia obsługę tworzenia i likwidacji obiektów, odpowiada za obsługę komunikatów, zawiera informacje o typie klasy oraz dane RTTI jej publikowanych właściwości.

Mechanizm RTTI pomaga w ustaleniu typu obiektu w czasie działania aplikacji, nawet w przypadku, gdy w kodzie używa się wskaźnika bądź referencji do tego obiektu. Przykładem może być przekazanie każdemu zdarzeniu wskaźnika do klasy TObject. Może to być zdarzenie polegające na wciśnięciu przycisku myszy lub wybraniu jakiegoś obiektu. Użycie RTTI umożliwia przeprowadzenie rzutowania typu (dynamic_cast) w celu wykorzystania obiektu bądź określenia jego typu. Możliwe jest też sprawdzenie typu obiektu przy użyciu operatora typeid. Operator dynamic_cast omówimy w dalszej części tego rozdziału. Dodatkowe informacje na ten temat można znaleźć w plikach pomocy C++Buildera dotyczących języka programowania (C++Builder Language Guide).

TObject jest przodkiem wielu nietrwałych klas danych, „opakowań” i strumieni, m.in.: TList, TStack, TPrinter, TRegistry, TStream oraz wielu innych.

Pojęcie danych trwałych (ang. persistent data) w terminologii VCL odnosi się do mechanizmu przechowywania wartości właściwości. Najprostszym przykładem może być napis znajdujący się na przycisku czy treść etykiety. W czasie tworzenia programu wprowadza się treść napisu w oknie inspektora obiektów. Treść ta może być zmieniana w czasie opracowywania kodu i będzie dostępna w czasie działania programu. Jak widać, dane te są trwałe – nie ulegają utracie pomiędzy zakończeniem i ponownym uruchomieniem aplikacji. Po przeciwnej stronie można umiejscowić klasy nietrwałe. Termin ten odnosi się do prostych klas zaprojektowanych w celu wykonywania konkretnych funkcji, ale nie mogących zachować swojego stanu pomiędzy kolejnymi uruchomieniami aplikacji.

„Otoczkę” lub „opakowanie” (ang. wrapper) można opisać jako osłonę bardziej złożonych interfejsów programowych. Mechanizm ten pozwala tworzyć obiekty lub komponenty, które są łatwiejsze w użyciu i mogą być zastosowane w wielu projektach. Komponenty i inne obiekty w pewnym stopniu izolują programistę od API, dostarczając jednocześnie komfort używania prostych narzędzi, stwarzających niezwykle duże możliwości. W kolejnych rozdziałach omówimy dodatkowe informacje związane z tym tematem.

Innym powszechnie używanym obiektem potomnym TObject jest klasa Exception, udostępniająca wiele klas potomnych, służących do obsługi wyjątków. To właśnie dzięki niej możliwa jest obsługa błędów związanych z dzieleniem przez zero czy obsługą strumieni danych. Klasy tej można użyć do budowy wyspecjalizowanych klas na potrzeby tworzonej aplikacji.

Do innych istotnych klas wywodzących się od TObject należą: TPersistent, TComponent, TControl, TGraphicControl i TWinControl.

TPersistent dodaje do klasy TObject metody umożliwiające obiektom zapisanie stanu przed dokonaniem ich likwidacji i odtworzenie go w chwili ponownego tworzenia. Klasa TPersistent odgrywa ważną rolę w tworzeniu komponentów, zawierających klasy użytkownika jako właściwości. Jeśli właściwość musi być zapisywana i odtwarzana ze strumienia, powinna zostać wyprowadzona raczej z TPersistent, a nie TObject. W gałęzi struktury klas zawierającej TPersistent można znaleźć różne rodzaje klas, w tym najbardziej popularne: TCanvas, TClipboard, TGraphic, TGraphicsObject i TStrings. Od klasy TPersistent pochodzi klasa TComponent, będąca podstawą wszystkich komponentów biblioteki VCL.

Termin „strumieniowanie właściwości” (ang. streaming) odnosi się do mechanizmu, dzięki któremu wartości właściwości obiektu są zapisywane w pliku formularza. Gdy projekt otwierany jest ponownie, wartości właściwości są odtwarzane ze strumienia, co przywraca ich poprzedni stan.

Obiekty klasy TComponent stanowią podstawę tworzenia aplikacji przy użyciu C++Buildera. Poszczególne komponenty mogą być umieszczane w palecie komponentów, służyć jako przodkowie innych komponentów i sterować nimi

było „przodkowie elementów sterujących” (oryginał: control other components, czyli sterować działaniem innych komponentów)

. Mechanizm „strumieniowania” pozwala przechowywać wartości ich właściwości.

Istniejące komponenty dzieli się na wizualne i niewizualne

w BCB3 pisałem o „widzialnych” i „niewidzialnych” – moim zdaniem to jest to samo (w każdym razie znaczeniowo – na pewno), a znacznie bardziej po polsku. Ale ponieważ w środowisku na zasadzie kalki przyjęło się słowo „wizualny” (i tak zapewne jest w tłumaczeniu AG), nie będę specjalnie protestował...

. Komponenty niewizualne nie posiadają reprezentacji graficznej i dlatego dziedziczą bezpośrednio z klasy TComponent. W przypadku komponentów wizualnych niezbędna jest możliwość ich prezentacji na ekranie. Użytkownik programu musi mieć też możliwość oddziaływania na nie. Procedury rysowania oraz zdarzenia związane z oknem programu, niezbędne do zdefiniowania komponentów wizualnych, dodaje klasa TControl. Komponenty te są podzielone na dwie grupy: okienkowe (klasa TWinControl) i nieokienkowe (klasa TGraphicControl).

Komponenty klasy TGraphicControl odpowiadają za rysowanie samych siebie, ale nigdy nie mogą zostać wybrane. Przykładem mogą być klasy: TImage, TLabel, TBevel i TShape.

Klasa TWinControl obejmuje komponenty podobne do zawartych w klasie TGraphicControl. Różnica polega na tym, że komponenty klasy TWinControl mogą być wybierane. W rezultacie użytkownik programu ma możliwość oddziaływania na nie. Są to tak zwane komponenty okienkowe (ang. windowed controls). Komponent okienkowy posiada uchwyt okna i może zawierać inne komponenty lub być ich rodzicem

parent to rodzic, a nie przodek. Te dwa pojęcia są zupełnie różne – przodek (ancestor) oznacza klasę bazową w hierarchii klas, natomiast rodzic to okno zawierające dany komponent

.

Więcej informacji na temat komponentów wizualnych i niewizualnych znajduje się w rozdziale 7.

Tworzenie aplikacji przy użyciu istniejących obiektów

Dzięki obiektowej architekturze C++Buildera programista ma możliwość szybszego tworzenia aplikacji z wykorzystaniem już istniejących obiektów i klas. Okno inspektora obiektów udostępnia ich publikowane właściwości, co znacząco ułatwia  tworzenie aplikacji.

Edycja i przeglądanie publikowanych właściwości komponentów

było „właściwości komponentów publikowanych”

stanowią jedynie niektóre spośród licznych możliwości inspektora obiektów. Rysunek 6.1 przedstawia okno inspektora obiektów, prezentujące wspólne właściwości elementów sterujących: TLabel, TEdit, TButton i TCheckBox. Analizując hierarchię powyższych elementów sterujących, można zauważyć, że element TLabel pochodzi od klasy TGraphicControl, pozostałe natomiast od TWinControl. Wspólnym przodkiem wszystkich czterech elementów sterujących jest zatem klasa TControl, ponieważ zarówno TGraphicControl oraz TWinControl od niej dziedziczą.

Rysunek 6.1.

Wspólne właściwości kilku jednocześnie wybranych komponentów

 

Na rysunku 6.1 przedstawione są wszystkie właściwości, które komponenty te wspólnie odziedziczyły od klasy TControl. Dokonując zmiany jednej właściwości w przypadku zaznaczenia kilku komponentów, zmienia się jednocześnie właściwości ich wszystkich.

Rysunek 6.2 przedstawia sposób użycia inspektora obiektów do ustawiania właściwości komponentu, będącej wskaźnikiem do innego komponentu. Należy zwrócić uwagę na fakt, że w okienku właściwości dostępne są jedynie te komponenty, które do danej właściwości „pasują” – innymi słowy pochodzą od tej samej klasy, od której została wyprowadzona edytowana właściwość

Czegoś tu nie rozumiem – w oryginale jest mowa o „lokalizacji komponentów wyprowadzonych od typu danej właściwości”, w tłumaczeniu nie widzę odpowiedniości, chociaż ma ono sens. Poza tym chodzi chyba o „edytowaną właściwość” a nie „edytowany element” (a właściwie modyfikowaną, zmienianą itd. a nie „edytowaną”,  bo to też kalka).

.

Rysunek 6.2.

Komponenty

tu raczej nie ma mowy o żadnym „rozpoznawaniu”, po prostu wyświetla się ich listę.

potomne względem typu właściwości

 

Wykorzystanie istniejących obiektów i klas do tworzenia obiektów potomnych pozwala na uzupełnianie ich o dodatkowe możliwości

„funkcjonalność” nie występuje chyba w liczbie mnogiej.

, umożliwiając jednocześnie wykorzystanie klas bazowych przez obiekty potomne mające inne specyficzne cechy

dość trudno było mi się z tłumaczenia domyślić sensu oryginału...

. Niektórzy programiści mogą argumentować, że dodatkowa klasa oraz odpowiadające jej dane RTTI, zawarte w hierarchii, powodują jedynie powiększenie aplikacji o zbędne wiersze kodu. Nadmiar ten jest jednak wart korzyści, jakich dostarcza model obiektowy. Obiekty i komponenty dostarczone wraz z C++Builderem 5 mogą znacząco się różnić, a jednak mieć te same lub podobne cechy odziedziczone od przodka. Pozwala to na znaczne usprawnienie tworzenia aplikacji.

Jak używać biblioteki VCL

Zrozumienie różnic pomiędzy zwykłymi klasami i obiektami a elementami biblioteki VCL jest bardzo istotne. Biblioteka VCL ma swoje korzenie w języku Object Pascal. W rezultacie wszystkie obiekty biblioteki VCL są tworzone na stercie (ang. heap), a odnosi się do nich,

nie „raczej” – wszystkie obiekty VCL tworzone są na stercie, więc o żadnym „raczej” nie ma mowy.

używając wskaźników, nie zaś odwołań statycznych. Konieczność tworzenia i używania obiektów w taki sposób jest cechą charakterystyczną biblioteki VCL. W przypadku obiektów typowych dla języka C/C++ sposób odwołania nie ma znaczenia.

Zasadę tę zilustrujemy przykładem. Najpierw pokażemy, jak utworzyć standardową klasę w języku C++ na stosie i na stercie, następnie przedstawimy sposób tworzenia obiektu biblioteki VCL.

Oto definicja klasy:

class MyClass

{

private:

    int MyVar;

public:

    MyClass(void);

};

Poniżej tworzymy klasę na stosie. W tym przypadku procedura zwalniania pamięci zajmowanej przez obiekt wykonywana jest automatycznie.

 

MyClass Tmp;

Tmp.MyVar = 10;

Teraz przyjrzymy się tworzeniu przykładowej klasy na stercie – tym razem usunięcie jej egzemplarza jest naszym obowiązkiem. W przeciwnym razie będziemy mieli do czynienia z „wyciekiem

ja staram się pisać o niezrównoważonej konsumpcji pamięci (brzmi formalnie, ale dokładnie o to chodzi), jest mniej żargonowo.

” pamięci.

 

MyClass *Tmp;

Tmp = new MyClass;

Tmp->MyVar = 10;

// Fragment kodu programu, gdzie nasz obiekt jest używany

delete Tmp;

Obiekty biblioteki VCL tworzone są na stercie, podobnie jak widać to w drugim przykładzie. Próba utworzenia obiektu VCL na stosie spowoduje, że kompilator sprzeciwi się temu, wyświetlając komunikat VCL style classes must be constructed using operator new.

C++Builder 5 posiada zabezpieczenie przed automatycznym usuwaniem obiektów posiadających właściciela

Nie. „Provision” to nie „protection”, a w oryginale nie chodzi o zabezpieczanie przed automatycznym usuwaniem obiektów posiadających właściciela, lecz automatyczne usuwanie obiektów posiadających właściciela, przez tego ostatniego w chwili jego usunięcia. Sens obu sformułowań jest dokładnie przeciwny.

. Załóżmy, że dynamicznie tworzony jest obiekt TLabel, a jego właściciela wskazuje wskaźnik this.

TLabel *MyLabel = new TLabel(this);

W tym przypadku nie trzeba obawiać się „wycieku” pamięci, gdyż obiekty posiadające właściciela

określenie „obiekty tego typu” jest nieco dwuznaczne, chociaż oryginał też nie grzeszy precyzją.

nie muszą się troszczyć o zwolnienie zajmowanych zasobów. Biblioteka VCL ma wbudowany mechanizm zwalniania zasobów zajmowanych przez wszystkie obiekty posiadające właściciela

nie „przodka” (zob. uwaga 8), bo to pojęcie dotyczy wyłącznie hierarchii klas i nie ma nic wspólnego z „owner”.

, zanim ten ostatni zostanie zniszczony. Do tej pory unikaliśmy terminu rodzica

nie „przodka” (zob. uwagi 8, 18). Podsumowując:

ancestor = przodek (w hierarchii obiektowej i tylko tam)

parent = rodzic

owner = właściciel

obiektu, zajmiemy się tym nieco później.

Komponenty niewizualne posiadają jedynie właścicieli (ang. owner), natomiast wizualne właścicieli i rodziców (ang. parent). Najłatwiejszym sposobem rozróżnienia obu terminów jest wyobrażenie sobie właściciela jako tworzącego, a rodzica jako „podstawy”, dzięki której obiekt istnieje. Załóżmy, że na formularzu znajduje się komponent TPanel, a na nim trzy etykiety. W zależności od tego, kto je utworzył, istnieją dwie możliwości.

W pierwszej z nich etykiety są umieszczane na panelu w czasie tworzenia aplikacji – wtedy panel jest przodkiem każdej z nich, ale właścicielem jest aplikacja. Podczas zamykania aplikacji biblioteka VCL zapewnia likwidację wszystkich obiektów będących „dziećmi”, tj. panelu i trzech etykiet.

W drugim przypadku można umieścić na formularzu „zagregowany” komponent (utworzony z wielu innych komponentów) – na przykład panel z trzema etykietami. Komponent taki tworzy panel, a następnie umieszcza na nim trzy etykiety. Panel jest w dalszym ciągu rodzicem etykiet, jednak teraz właścicielem panelu i zawartych na nim etykiet jest komponent, który został umieszczony na formularzu. Jeżeli zostanie on zlikwidowany, panel i etykiety będą usunięte razem z nim.

Cecha ta jest bardzo przydatna dla obiektów tworzonych w czasie projektowania aplikacji. Jeśli jednak obiekty tworzone są w czasie działania aplikacji, powinno się je usuwać w sposób jawny. Zaniechanie tej czynności nie spowoduje wprawdzie „wycieku” pamięci, jednak praktyka ta poprawia przejrzystość kodu i ułatwia zrozumienie jego działania

pierwotna postać tego zdania była moim zdaniem nielogiczna

. Poniżej przedstawiamy sposób tworzenia obiektów w czasie działania aplikacji.

TPanel *Panel1;

TLabel *Label1, *Label2, *Label3;

Panel1 = new TPanel(this);

Panel1->Parent = Form1;

Label1 = new TLabel(this);

Label1->Parent = Panel1;

Label2 = new TLabel(this);

Label2->Parent = Panel1;

Label3 = new TLabel(this);

...

Zgłoś jeśli naruszono regulamin