Delphi Kompendium Roz14


Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Logowanie | Rejestracja | Forum | Pomoc | Reklama | Szukaj
Strona główna :: Delphi :: Kompendium
RSS | Forum | Pastebin |
Regulamin | Pomoc | Usuń
Rozdział 14
Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
cookies | Prawa autorskie |
Czas generowania strony: 0.9116 sek. (zapytań SQL:
Edytuj Historia Kontakt | Reklama
12)
Komponenty VCL i CLX
Zanim przejdziemy do właściwego procesu tworzenia komponentów, konieczne będzie dokładne zapoznanie się
z zasadami ich działania. Co prawda zarówno o klasach, jak i o VCL mówiłem już w rozdziale 3., lecz tamta
wiedza była konieczna do dalszego projektowania aplikacji w Delphi. Tym razem poznasz architekturę
komponentów VCL oraz CLX ze strony projektanta ? dowiesz się, jak to wszystko działa i jak jest ze sobą
połączone.
Spis treści
1 Czym jest komponent?
2 VCL
3 CLX
3.1 Tworzenie aplikacji opartych na CLX
3.2 CLX a VCL
3.3 Architektura CLX
4 Windows a Linux
4.1 Kompilacja warunkowa
5 Rodzaje komponentów
5.1 Komponenty wizualne
5.2 Komponenty niewizualne
5.3 Komponenty graficzne
6 Hierarchia komponentów
6.1 TObject
6.1.1 Metody klasy TObject
6.1.1.1 ClassName
6.1.1.2 ClassNameIs
6.1.1.3 ClassInfo
6.1.1.4 ClassParent
6.1.1.5 ClassType
6.1.1.6 FieldAddress
6.1.1.7 MethodAddress
6.2 TPersistent
6.2.1 Metoda Assign
6.2.2 Metoda AssignTo
6.2.3 Metoda DefineProperties
6.3 TComponent
6.3.1 Właściwości klasy TComponent
6.3.1.1 ComponentCount
6.3.1.2 ComponentIndex
6.3.1.3 Components
6.3.1.4 ComponentState
6.3.1.5 Name
6.3.1.6 Owner
6.3.2 Metody klasy TComponent
6.4 TControl
6.4.1 Właściwości klasy TControl
6.5 TWinControl i TWidgetControl
6.5.1 Właściwości klas TWinControl i TWidgetControl
6.5.2 Zdarzenia
6.6 Klasy TCustom
6.7 TGraphicControl
7 Budowa komponentu
7.1 Właściwości
7.1.1 Rodzaje właściwości
7.2 Zdarzenia
7.3 Metody
8 RTTI
8.1 Właściwości obiektu
8.2 Dokładniejsze informacje o obiekcie
9 Podsumowanie
W tym rozdziale:
dowiesz siÄ™, jak funkcjonuje klasa VCL;
dowiesz siÄ™, czym jest biblioteka CLX;
poznasz elementarne metody i właściwości podstawowych klas.
Czym jest komponent?
Delphi jako środowisko wizualne udostępnia różne kontrolki ? tzw. komponenty, służące do szybkiego
projektowania aplikacji. Komponenty są ?klockami?, którymi posługuje się programista w procesie budowania
aplikacji.
Patrząc na komponenty ze strony projektanta aplikacji, mamy do czynienia z wygodnymi i łatwymi w obsłudze
narzędziami. Nie interesuje nas, jak one działają i jaki kod znajduje się ?w środku?. Ważne są zdarzenia,
metody, właściwości i zadania, jakie spełniają. W rzeczywistości wiele komponentów jest ze sobą powiązanych
i korzysta z tych samych modułów, klas i bibliotek.
Kluczem do poznania VCL oraz CLX jest zrozumienie zasady działania różnych typów danych, poznanie
1 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
hierarchii klas itp.
VCL
VCL to Visual Component Library (wizualna biblioteka komponentów). Programowanie w Delphi nie byłoby takie
łatwe dla początkujących, gdyby nie VCL. Poprzednikiem VCL była biblioteka OWL (Object Windows Library),
znajdująca się w Turbo Pascalu. Dla użytkownika wywołanie konstruktora Create to tylko jeden wiersz kodu,
powodujący utworzenie komponentu (obiektu). W rzeczywistości na podstawie kodu VCL wykonywany jest
szereg innych procedur, które ostatecznie dają efekt w postaci utworzenia komponentu.
CLX
Opublikowanie Delphi 6 było czymś niezwykłym ze względu na wprowadzenie biblioteki CLX (Component Library
for Cross-Platform), czyli międzyplatformowej biblioteki komponentów. Pojawienie się CLX wiązane jest z
wydaniem nowego środowiska programistycznego Kylix, przeznaczonego dla systemu operacyjnego Linux.
Programowanie w Kyliksie jest niezwykle podobne do programowania w Delphi ? nawet interfejs jest ten sam.
Kylix również ? tak jak Delphi ? wykorzystuje język Object Pascal, co mogło trochę dziwić w momencie edycji
produktu. W końcu Linux jest platformą, na której króluje C++.
W każdym razie w związku z pojawieniem się Kyliksa oraz biblioteki CLX przekroczona została pewna bariera ?
od tej pory możliwe jest tworzenie aplikacji zgodnych zarówno z systemem Windows, jak i Linuks.
Tworzenie aplikacji opartych na CLX
Aby stworzyć aplikację działającą w oparciu o CLX, wystarczy z menu File wybrać polecenie New/CLX
Application. Wówczas zmieniona zostanie zawartość palety komponentów oraz edytora kodu.
CLX a VCL
Sam akronim VCL może dawać do zrozumienia, że mamy do czynienia z całkowicie wizualną biblioteką. W
rzeczywistości tak nie jest, bo VCL zawiera również komponenty, które nie są widoczne, ale jednak odpowiadają
za wykonywanie jakichś czynności. Pisząc programy działające w systemie Windows, korzystamy ze
standardowych kontrolek tego systemu (chociażby kontrolka TButton), zawartych w bibliotece User32.dll lub
CommCtrl32.dll. Natomiast w przypadku CLX komponenty z tej biblioteki sÄ… oparte na tzw. widgetach,
zawartych w bibliotece Qt. Owa biblioteka zawiera niezależne klasy, które swoim wyglądem oraz
funkcjonalnością przypominają standardowe kontrolki Windows.
Słowo widget jest skrótem słów Window Gadget.
Architektura CLX
Architektura CLX jest nieco bardziej złożona niż VCL. Mamy tu bowiem kilka grup komponentów. Oprócz grupy
wizualnej ? VisualCLX ? istnieją jeszcze komponenty BaseCLX (moduły wspólne dla Delphi i Kyliksa), DataCLX
(komponenty zapewniające dostęp do technologii dbExpress ? będziemy o tym mówić w kolejnej części książki)
oraz NetCLX (technologie internetowe).
VCL jest zbudowany na bazie kontrolek WinAPI, zawartych m.in. w bibliotekach User32.dll oraz ComCtrl32.dll.
W przypadku CLX grupa komponentów VisualCLX jest oparta na bibliotece Qt.
Hierarchia klas jest ona bardzo podobna do hierarchii komponentów VCL. Dodano parę nowych klas, inne
zamieniono miejscami, lecz starano się zachować maksymalną kompatybilność pomiędzy CLX i VCL ? tak, aby
przenoszenie kodu odbywało się z jak najmniejszym nakładem pracy.
Windows a Linux
Można by przypuszczać, że dla programisty tworzącego swoje aplikacje w języku Object Pascal nie ma
znaczenia, na jakiej platformie będzie uruchamiany ów program. Oczywiście należy jednak pamiętać o pewnych
elementach charakterystycznych dla danego systemu. Przykładem może być konieczność zwracania uwagi na
nazewnictwo plików w Linuksie ? w systemie tym rozróżniane są np. nazwy modułów na liście uses.
W systemie Linux używany jest inny znak separatora katalogów. W Windows jest to znak backslash (\), a
w Linuksie ? slash (/). Właściwy dla konkretnej platformy znak można odczytać ze stałej PathSeparator.
Pamiętasz, jak podczas omawiania bibliotek DLL wspominałem o konwencjach wywołania stdcall, safecall
i cdecl? W Kyliksie należy zmienić tę klauzulę na cdecl, gdyż tylko taka forma jest prawidłowa.
Tworząc programy w Kyliksie, należy unikać modułów oraz funkcji charakterystycznych dla Windows ?
np. modułów Windows, ActiveX, ComServ, ComObj itp. ? gdyż nie odnajdziemy tam takich technologii
jak BDE, COM czy ActiveX.
Bardzo ważne jest zapamiętanie, że w Linuksie nie możemy korzystać z komunikatów. Niedopuszczalne
jest zatem wysyłanie komunikatów czy ich przechwytywanie ? np. WM_CHAR itp.
Większość klas nazywa się tak samo. Pamiętaj jednak, że klasa TWinControl w CLX nazywa się
TWidgetControl. Ze względów kompatybilności zachowano jednak również klasę TWinControl, która jest
równoważna klasie TWidgetControl.
Unikaj stosowania funkcji typowych dla Windows: Win32Check, RaiseLastWin32Error.
W systemie Linux nie ma Rejestru ? nie możesz więc korzystać z klasy TRegistry.
Nazwy modułów CLX rozpoczynają się od litery Q ? np. QControls.
System Linux nie używa liter do określania dysków, tak jak ma to miejsce w Windows. Wszystko opiera
się na charakterystycznym systemie plików, gdzie istnieją tylko katalogi główne ? np. /usr
Kompilacja warunkowa
Zarówno w Delphi, jak i w Kyliksie istnieje możliwość wprowadzenia charakterystycznego dla danej platformy
kodu. Wszystko dzięki tzw. symbolom kompilacji warunkowej.
{$IFDEF LINUX}
// kod dla Linuksa
{$ENDIF}
Dla systemu Linux takim symbolem jest LINUX, a dla Windows może to być WIN32 i MSWINDOWS:
{$IFDEF WIN32}
// kod dla Windows
{$ENDIF}
2 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Dzięki takiemu prostemu rozwiązaniu można wprowadzać charakterystyczne dla danej platformy elementy
kodu.
Rodzaje komponentów
Komponenty w Delphi dzielą się na wizualne oraz niewizualne ? bynajmniej nie chodzi tutaj o właściwość
Visible, której zmiana na True powoduje ukrycie komponentu.
Komponenty wizualne
Nieprzypadkowo dla określenia komponentu wizualnego często używam w tej książce słowa kontrolka (control).
Należy zdać sobie sprawę z tego, że kontrolka to obiekt wizualny, będący w stanie odbierać komunikaty od
użytkownika. Kontrolki mają właściwości mogące określić ich położenie względem projektanta formularzy. Mogą
być także aktywowane (focus). Komponenty wizualne wywodzą się z klasy TControl. Przykładami kontrolek
wizualnych sÄ… TButton, TLabel, TListView, TComboBox itp.
Komponenty niewizualne
Komponentem jest każdy obiekt, mogący znalezć się w palecie komponentów. Niektóre z tych obiektów są
widoczne po uruchomieniu programu, niektóre jednak nie. Komponenty niewidoczne wywodzą się z klasy
TComponent, lecz mimo tego, że ich nie widać, pełnią inną funkcję, często o wiele ważniejszą niż rola obiektów
wizualnych. Przykładem komponentów niewidocznych jest komponent TTimer, TOpenDialog (ogólnie wszystkie
komponenty z palety Dialogs) oraz komponenty z palety Indy.
Komponenty graficzne
Niektóre komponenty Delphi mimo swego charakteru wizualnego nie posiadają zdolności interakcji z
użytkownikiem ? nie mogą więc stać się aktywne. Nie posiadają także uchwytu, a ich klasą bazową jest
TGraphicControl.
Przykładem takich komponentów są TPaintBox i TImage.
Hierarchia komponentów
Na rysunku 14.1 przedstawiona została hierarchia komponentów VCL. Naturalnie jest to tylko fragment
ogromnej pajęczyny klas.
Rysunek 14.1. Hierarchia komponentów VCL
W CLX ze względów kompatybilności starano się zachować identyczne nazewnictwo klas. Różnica pojawia się w
klasie TWinControl, która w CLX nosi nazwę TWidgetControl. Pamiętaj o tym, że w CLX nie ma klasy
TRegistry, lecz możesz korzystać z klasy TRegINIFile.
TObject
Klasa TObject jest podstawowym fundamentem całej struktury VCL oraz CLX. Zawiera wiele podstawowych
metod, obecnych we wszystkich klasach. Wszystko dzięki dziedziczeniu ? w czasie, gdy pozostałe klasy
dziedziczÄ… po klasie TObject, przejmujÄ… jej wszystkie metody. Klasa ta odpowiada za tak podstawowe
czynności, jak:
tworzenie i usuwanie instancji komponentu,
alokację i zwalnianie potrzebnej pamięci,
obsługę komunikatów,
zwracanie informacji na temat rodzaju klasy, nazwy itp.
Metody klasy TObject
Klasa TObject definiuje jedynie metody, które mogą być pózniej używane przez wszystkie inne kontrolki VCL.
Spis najważniejszych metod klasy TObject znajduje się poniżej.
ClassName
class function ClassName: ShortString;
Owa funkcja zwraca nazwę klasy ? przykład:
ShowMessage(Button1.ClassName);
Powyższy kod spowoduje wyświetlenie rzeczywistej nazwy klasy, czyli TButton.
ClassNameIs
class function ClassNameIs(const Name: string): Boolean;
Funkcja sprawdza, czy podana w parametrze klasa odpowiada tej, z której jest wywoływana funkcja. Najlepiej
objaśnić to na przykładzie:
3 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Button1.ClassNameIs(?TButton?); // funkcja zwróci True
Button1.ClassNameIs(?TObject?); // funkcja zwróci False
ClassInfo
class function ClassInfo: Pointer;
Funkcja zwraca wskaznik do tablicy zawierającej informacje takie jak typ obiektu, właściwości itp. Wrócimy do
tego nieco pózniej.
ClassParent
class function ClassParent: TClass;
Informacja o klasie bazowej dla naszego komponentu jest zwracana przez funkcję ClassParent. Dzięki owej
funkcji możemy odwołać się do innych właściwości klasy bazowej, nie znając jej podczas tworzenia programu.
ClassType
function ClassType: TClass;
Funkcja działa podobnie jak ClassParent. Jedyna różnica polega na tym, że zwraca ona informacje na temat
klasy, z której jest wywoływana ? np.:
ShowMessage(Button1.ClassType.ClassName);
Po wykonaniu takiego kodu w okienku wyświetlony zostanie napis TButton. Inna sprawa, że taki sam efekt
uzyskamy, korzystajÄ…c z funkcji ClassName.
FieldAddress
function FieldAddress(const Name: ShortString): Pointer;
Funkcja FieldAddress zwraca wskaznik do właściwości znajdującej się w pamięci. Nazwa właściwości musi
zostać podana w parametrze.
MethodAddress
class function MethodAddress(const Name: ShortString): Pointer;
Funkcja działa podobnie jak FieldAddress, tyle że ta zwraca wskaznik do metody umieszczonej w pamięci ? nie
zaś właściwości.
Więcej opisów metod klasy TObject możesz znalezć w systemie pomocy Delphi lub (w
języku polskim) pod adresem http://4programmers.net/Delphi
TPersistent
Klasą drugą po TObject w całej hierarchii jest TPersistent. Oczywiście większość metod dziedziczy po
TObject, ale wprowadza także swoje metody.
Klasy TPersistent możesz użyć jako klasy bazowej w momencie, gdy deklarujesz klasy, które nie mają być
komponentem. Jaka jest więc różnica między TObject a TPersistent? Otóż klasa TPersistent może być
przodkiem dla wszystkich klas mogących zapisywać wartości do właściwości, które następnie mogą być
przechowywane w pliku *.dfm (lub *.xfm ? dla Kyliksa).
Metoda Assign
procedure Assign(Source: TPersistent); virtual;
Metoda Assign, obecna w klasie TPersistent, powoduje skopiowanie danych (właściwości) z jednego obiektu
do drugiego.
Metoda AssignTo
procedure AssignTo(Dest: TPersistent); virtual;
AssignTo działa odwrotnie niż procedura Assign. Powoduje skopiowanie danych do innego obiektu, określonego
w parametrze Dest.
Metoda DefineProperties
procedure DefineProperties(Filer: TFiler); virtual;
Procedura DefineProperties umożliwia przechowanie w pliku *.dfm dodatkowych danych ? tym pojęciem
4 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
zajmiemy siÄ™ kolejnym rozdziale.
TComponent
Klasa TComponent, jak można wywnioskować z nazwy, jest przodkiem wszystkich komponentów. Komponenty
dziedziczÄ…ce po TComponent mogÄ…:
być zintegrowane z IDE Delphi, w tym mogą być umieszczone na palecie komponentów;
jeden obiekt typu TComponent może posiadać inny obiekt (być jego rodzicem);
komponenty mogą być konwertowane na obiekty COM lub kontrolki ActiveX.
Obiekty COM sÄ… wynalazkiem firmy Microsoft, stÄ…d obecne sÄ… jedynie na platformie Windows.
Klasa TComponent wprowadza szereg nowych właściwości oraz metod, obecnych pózniej we wszystkich
komponentach (w końcu wszystkie komponenty pochodzą od klasy TComponent).
Właściwości klasy TComponent
Klasa TComponent zawiera wiele ciekawych właściwości, które przedstawiłem poniżej.
ComponentCount
property ComponentCount: Integer;
Właściwość ComponentCount zwraca liczbę obiektów umieszczonych na danym komponencie (rysunek 14.2.):
procedure TMainForm.Button1Click(Sender: TObject);
begin
Application.MessageBox(PChar('Na formularzu znajdujÄ… siÄ™ ' + IntToStr(ComponentCount) + '
komponenty),
'Tak', MB_OK)
end;
Rysunek 14.2. Program demonstrujący znaczenie właściwości ComponentCount
ComponentIndex
property ComponentIndex: Integer;
Inna właściwość klasy TComponent ? Components[] to tablica komponentów. Do konkretnego obiektu można się
odwołać, podając jego indeks. Taki indeks natomiast jest zwracany przez właściwość ComponentIndex.
ShowMessage(IntToStr(
Button1.ComponentIndex));
W powyższym przypadku w okienku pojawi się cyfra 1 jako numer identyfikacyjny dla komponentu Button1.
Components
property Components[Index: Integer]: TComponent;
Jak wspomniałem powyżej, jest to tablica wszystkich elementów (komponentów) umieszczonych na danym
obiekcie. Do konkretnej kontrolki można się odwołać, podając w nawiasie kwadratowym jej indeks:
Components[1].Free;
Powyższy kod spowoduje zniszczenie obiektu określonego numerem 1.
ComponentState
property ComponentState: TComponentState;
Sygnalizuje stan komponentu. Może zwrócić wartości takie, jak przedstawione w tabeli 14.1.
Tabela 14.1. Możliwe elementy typu TComponentState
Element Opis
csDesigning Komponent jest w trakcie projektowania, np. jego właściwości są zmieniane
csDestroying Komponent za chwilÄ™ zostanie zniszczony
csFixups Komponent jest podłączony do innego komponentu znajdującego się na innym formularzu
csLoading Komponent jest właśnie ładowany (tworzony)
5 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
csReading Komponent odczytuje swoje właściwości ze strumienia (pliku *.dfm)
csWriting Komponent zapisuje właściwości do strumienia
Name
property Name: TComponentName;
Definiuje unikatową w ramach formularza nazwę dla komponentu. Wartość jest przeznaczona zarówno do
zapisu, jak i do odczytu.
Owner
property Owner: TComponent;
Właściwość Owner jest wskazaniem obiektu-rodzica. Jeżeli przykładowo komponent kkjest umieszczony na
formularzu Form1, to dzięki takiej konstrukcji:
Button1.Owner...
można odwołać się do formularza Form1.
Metody klasy TComponent
Nowych metod wprowadzonych wraz z klasą TComponent jest bardzo wiele. Większość z nich być może nigdy
nie zostanie przez Ciebie użyta, gdyż służą np. do określenia pewnego stanu, w jakim ma znajdować się
komponent.
Na pewno najważniejszą metodą klasy TComponent jest konstruktor, który w odróżnieniu od klasy TObject
posiada dodatkowy parametr ? rodzica, czyli obiekt, na którym ma zostać utworzony nowy komponent.
constructor Create(AOwner: TComponent); virtual;
Utworzony w ten sposób konstruktor powinien zostać zwolniony metodą Free.
Do zwalniania komponentu nigdy nie używaj metody Destroy. Procedura Free przed
rzeczywistym zwolnieniem sprawdza, czy obiekt nie został już wcześniej zwolniony, co
zapobiega pojawieniu się błędu typu Access Violation.
Oto przykład utworzenia komponentu typu TButton w sposób dynamiczny:
procedure TForm1.btnMakeClick(Sender: TObject);
var
MyButton : TButton;
begin
MyButton := TButton.Create(Form1);
MyButton.Parent := Form1; // określenie rodzica
MyButton.Caption := 'Przycisk 1';
end;
Rodzicem komponentu ustanawiamy formularz Form1. Jeżeli nie określiliśmy dokładniejszych danych ? np.
położenia komponentu ? zostanie on umieszczony w lewym, górnym rogu formularza.
Zwróć uwagę, że w powyższym kodzie nie ma instrukcji zwalniającej komponent ? Free. Wszystko dzięki temu,
że rodzicem nowego obiektu został formularz ? Form1. Przy zwalnianiu (zamykaniu) okna program najpierw
zwolni obiekty znajdujÄ…ce siÄ™ na nim, czyli m.in. nasz przycisk.
Ciekawą metodą klasy TComponent jest procedura FindComponent. Wspominam tu o niej, gdyż być może często
będziesz korzystał z jej zalet. Umożliwia ona bowiem odnalezienie jakiegoś komponentu na formularzu i
odwołanie się do konkretnych jego metod ? oto przykład:
procedure TMainForm.btnMakeClick(Sender: TObject);
begin
Randomize;
TEdit(FindComponent('Edit' + IntToStr(Random(3)+1))).Text := 'Nowa wartość';
end;
Powyższy kod spowoduje losowe odwołanie się do którejś z trzech umieszczonych na formularzu kontrolek typu
TEdit ki zmianę jej właściwości Text (rysunek 14.3).
Rysunek 14.3. Losowe odwołanie się do której z kontrolek TEdit
TControl
Kolejną klasą w hierarchii obiektów VCL jest klasa TControl. Reprezentuje ona komponenty wizualne (jest
podstawową klasą dla tego rodzaju komponentów). Każdy obiekt typu TControl posiada dodatkowe właściwości
określające położenie komponentu: Top (położenie w pionie), Left (położenie w poziomie), Width (szerokość) i
Height (wysokość).
Właściwości klasy TControl
6 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Najważniejsze z właściwości (których nazwy możesz już pamiętać, gdyż są one wyświetlane w Inspektorze
Obiektów) przedstawiłem w tabeli 14.2.
Tabela 14.2. Najważniejsze właściwości klasy TControl
Właściwość Opis
Align Określa wyrównanie komponentu względem obiektu-rodzica
AutoSize Określa wyrównanie komponentu względem obiektu-rodzica
Caption Tytuł komponentu (tekst wyświetlany na obiekcie)
ClientHeight Rozmiar obszaru roboczego (wysokość)
ClientWidth Rozmiar obszaru roboczego (szerokość)
Color Kolor tła obiektu
Cursor Kursor wyświetlany po umieszczeniu wskaznika myszy nad obiektem
Enabled Określa, czy obiekt jest aktywny czy też nie
Font Czcionka używana przez komponent
Hint Wskazówka (etykietka podpowiedzi), pokazywana po umieszczeniu kursora nad obiektem
ShowHint Właściwość określa, czy podpowiedzi mają być wyświetlane
Visible Właściwość określa, czy komponent ma być widoczny podczas działania programu
Procedury i funkcje klasy TControl mają na celu przede wszystkim ustawienie danej wartości komponentu lub
odczytanie jej. Owa klasa posiada także wiele metod nakazujących w konsekwencji wystąpienie pewnego
zdarzenia (klasa TControl wprowadza zdarzenia, dzięki którym jesteśmy w stanie zareagować na konkretne
sytuacje).
TWinControl i TWidgetControl
Klasy TWinControl (VCL) oraz TWidgetControl (CLX) sÄ… bazowymi klasami dla kontrolek takich, jak przycisk
(TButton) i lista rozwijalna (TComboBox). Owe klasy sÄ… klasami bazowymi dla bardzo wielu kontrolek w Delphi.
Charakteryzują się następującymi właściwościami:
Umożliwiają wyświetlanie tekstu (np. TMemo).
Umożliwiają odbieranie od użytkownika zdarzeń, reagowanie np. na naciśnięcie przycisku czy wpisywanie
znaków za pomocą klawiatury.
Kontrolki mogą być rodzicem innych obiektów.
Kontrolki pochodzÄ…ce z tych klas posiadajÄ… uchwyt (VCL) lub widget (CLX).
Właściwości klas TWinControl i TWidgetControl
Obie klasy definiują wiele właściwości, mających na celu zmodyfikowanie wyglądu danej kontrolki. Za przykład
może tu posłużyć właściwość BevelKind, która określa styl rysowania ramki dla komponentu. Do podobnych
właściwości tego typu należą także: BevelEdges, BevelInner, BevelOuter, BevelWidth i BorderWidth.
Ciekawą właściwością jest także Brush, która określa kolor i styl wypełniającego wnętrze komponentu.
Zdarzenia
Zdarzenia klasy TWinControl są związane z uzyskiwaniem dostępu do obiektu (aktywacja) czy też
wprowadzaniem tekstu z klawiatury. SÄ… to zdarzenia: OnDockDrop, OnDockOver, OnEnter, OnExit, OnKeyDown,
OnKeyPress i OnKeyUp.
Klasy TCustom
Wiele klas VCL ? np. TMemo ? posiada odpowiedniki w postaci klas bazowych, które rozpoczynają się
przedrostkiem Custom, np. TCustomMemo. Takie klasy pełnią wyłącznie rolę klas bazowych dla nowych
komponentów i nie są w praktyce wykorzystywane. Np. komponent TDBMemo także wywodzi się z klasy
TCustomMemo ? dzięki temu rozwiązaniu programiści oszczędzają wiele wierszy kodu, dziedzicząc w zwykły
sposób potrzebne metody i właściwości.
TGraphicControl
Jest to raczej specyficzny rodzaj kontrolek, a mianowicie komponent graficzny. Komponenty takie są oczywiście
komponentami wizualnymi, ale nie mogą być aktywne. Nie mogą także pełnić roli rodzica innych komponentów.
Śmiało można powiedzieć, że kontrolki tego typu są komponentami nieaktywnymi.
Natomiast każda kontrolka typu TGrpaphicControl ma przyporządkowane płótno (Canvas), którego
odświeżenie następuje za pomocą wywołania metody Repaint.
Budowa komponentu
Komponent w rzeczywistości jest klasą. Jest jednak klasą dość specyficzną, posiadającą określoną budowę, bez
której taka klasa nie byłaby po prostu komponentem.
Właściwości
W klasie właściwości służą do przechowywania w pamięci pewnej wartości. W przypadku komponentów wartości
standardowo wprowadzone w Inspektorze Obiektów są zapisywane w pliku *.dfm. Jednakże aby właściwości
były widoczne w Inspektorze Obiektów, należy użyć specjalnej sekcji ? published. O klasach mówiłem już w
rozdziale 3., lecz o sekcji published jedynie wówczas wspomniałem. Jeśli tworzysz komponenty i chcesz dodać
do nich właściwości, kod w sekcji published musi wyglądać tak:
TMojKomponent = class(TLabel)
private
FText : String;
procedure DoIt(Value : String);
published
properties Text : String read FText write DoIt;
7 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
end;
Właściwości w klasie musimy poprzedzać słowem kluczowym properties. Powyższy kod oznacza, że właściwość
Text będzie typu String, a jej odczytanie będzie równoważne z odczytaniem danych ze zmiennej FText.
Wszystko dzięki klauzuli read, po której następuje nazwa zmiennej umieszczonej w sekcji private (nie jest
konieczne umieszczenie zmiennej FText w sekcji public). Natomiast w przypadku, gdy użytkownik zechce
przypisać jakąś wartość właściwości Text, wywołaprocedurę DoIt. Nowa wartość zostanie przekazana do
procedury DoIt w parametrze Value.
Warto, abyś przyzwyczaił się do takiej konstrukcji w przypadku komponentów, gdyż w przyszłości często
będziemy ją stosowali.
Chcąc uczynić daną właściwość przeznaczoną tylko do zapisu lub tylko do odczytu, wystarczy pominąć jedną z
klauzul ? albo read, albo write.
Istnieje możliwość dodania na samym końcu słowa kluczowego default, które powoduje ustawienie domyślnej
wartości właściwości.
Rodzaje właściwości
Pracując już jakiś czas z Delphi, zapewne zauważyłeś, że nie wszystkie właściwości w Inspektorze Obiektów są
przedstawione tak samo. Niektóre proste właściwości służą jedynie do wpisywania nowej wartości i
odczytywania jej. Z kolei inne właściwości są przedstawione w postaci listy rozwijalnej. Tabela 14.3 przedstawia
różne typy właściwości.
Tabela 14.3. Typy właściwości
Typy
Opis
właściwości
Właściwość wyliczeniowa prezentowana jest w Inspektorze Obiektów w formie listy
Wyliczeniowa
rozwijalnej (Å‚Ä…czenie z typem Boolean) ? rysunek 14.4
Właściwość zbiorowa (set) jest prezentowana w formie listy rozwijalnej. Wartości w zbiorze
Zbiorowa można zmieniać na True lub False, w zależności od tego, czy właściwość przynależy do
zbioru czy też nie (rysunek 14.5)
Czasami zdarza się, że właściwość danego komponentu jest konkretnym obiektem (klasą).
Tak jest np. z właściwością Lines komponentu TMemo, która jest właściwością typu
Obiektowa
TStringList. Wówczas przy próbie edycji otwierany zostaje nowy edytor
Rysunek 14.4. Właściwość wyliczeniowa
Rysunek 14.5. Właściwość zbiorowa
Zdarzenia
Zdarzenia służą do zaprogramowania określonej reakcji w momencie zajścia jakiegoś wydarzenia (np. kliknięcia
myszą). Podczas projektowania własnych komponentów dość często będziesz zmuszony do przypisywania w
kodzie programu jakiegoś zdarzenia, tzw. procedury zdarzeniowej. Procedura zdarzeniowa musi mieć określone
parametry, zależne od rodzaju zdarzenia. Przykładowo procedura zdarzeniowa dla zdarzenia OnClick musi mieć
parametr Sender typu TObject. Zatem deklaracja takiej procedury będzie wyglądać tak:
private
procedure MyOnClick(Sedner: TObject);
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
MyButton : TButton;
begin
MyButton := TButton.Create(Form1);
MyButton.Parent := Form1;
MyButton.OnClick := MyOnClick; // przypisanie procedury zdarzeniowej
end;
...
W przypadku, gdyby procedura MyOnClick nie posiadała parametru Sender, próba kompilacji programu
zakończyłaby się komunikatem o błędzie: [Error] Unit1.pas(32): Incompatible types: 'Parameter lists differ'.
Metody
8 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Przypominam, że metody to inaczej procedury i funkcje zawarte w klasie. Aby owe metody były (w przypadku
komponentów) widoczne dla użytkownika komponentu, należy umieścić je w sekcji public.
RTTI
Zajmiemy się teraz pojęciem zwanym RTTI (Run Time Type Information), które umożliwia nam dostęp do
informacji na temat właściwości, zdarzeń poszczególnych komponentów ? wszystko podczas działania
skompilowanej aplikacji.
Właściwości obiektu
Funkcje, z których będziemy korzystać, znajdują się w pliku TypInfo.pas ? dodaj więc do listy uses moduł
TypInfo. W gruncie rzeczy pobieranie listy właściwości danego obiektu jest proste ? wystarczy skorzystać z
funkcji GetPropList. W rzeczywistości okazuje się, że czeka nas wiele pracy z wskaznikami.
Funkcja GetPropList w module TypInfo jest zadeklarowana następująco:
function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList): Integer;
Pierwszy parametr tej funkcji może zawierać wskazanie komponentu, którego będzie dotyczyć operacja ? w to
miejsce możemy np. wstawić instrukcję Button1.ClassInfo. Drugi parametr jest pewnego rodzaju filtrem.
Określa, jakie właściwości chcemy wyświetlić. Jest to właściwość typu zbiorowego, zadeklarowana następująco:
type
TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);
TTypeKinds = set of TTypeKind;
Możemy podać, jakie właściwości chcemy wyświetlić. Najlepszym rozwiązaniem jest wstawienie jako drugiego
parametru słowa tkProperties, które obejmuje wszystkie właściwości. Podsumowując, wywołanie funkcji
GetPropList może wyglądać tak:
GetPropList(btnGet.ClassInfo, tkProperties, PropList);
Zakładamy, że ostatni parametr ? PropList ? jest typu PPropList. Z kolei typ PPropList jest zadeklarowany
następująco:
PPropList = ^TPropList;
TPropList = array[0..16379] of PPropInfo;
A zatem okazuje się, że PPropList jest wskazaniem typu TPropList, który z kolei jest tablicą PPropInfo:
PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo;
GetProc: Pointer;
SetProc: Pointer;
StoredProc: Pointer;
Index: Integer;
Default: Longint;
NameIndex: SmallInt;
Name: ShortString;
end;
Wiem, wiem, że to wszystko jest zagmatwane ? jeden typ jest wskazaniem drugiego typu itp., ale musisz się
przyzwyczaić, że całe VCL w ten właśnie sposób jest zbudowane. Pełny kod programu został przedstawiony w
listingu 14.1, a program w trakcie działania ? na rysunku 14.6.
Listing 14.1. Pełny kod zródłowy modułu
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, ValEdit;
type
TMainForm = class(TForm)
btnGet: TButton;
vleValue: TValueListEditor;
procedure btnGetClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses TypInfo;
procedure TMainForm.btnGetClick(Sender: TObject);
var
PropList : PPropList;
i : Integer;
begin
GetMem(PropList, SizeOf(PropList^)); // zarezerwuj pamięć
try
9 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
// pobierz listę właściwości
GetPropList(btnGet.ClassInfo, tkProperties, PropList);
for I := 0 to High(PropList^) ?1 do
begin
if (PropList^[i] <> nil) then
vleValue.InsertRow(PropList^[i].Name, PropList^[i].PropType^.Name, True)
end;
finally
FreeMem(PropList);
end;
end;
end.
Rysunek 14.6. Właściwości komponentu TButton
Wywołując funkcję GetPropList w ten sposób:
GetPropList(btnGet.ClassInfo, tkProperties + [tkMethod], PropList);
spowodujemy, że pod uwagę zostaną wzięte także metody danego obiektu.
Dokładniejsze informacje o obiekcie
W poprzednim podpunkcie omówiłem zagadnienie związane z pobieraniem informacji na temat właściwości
danego obiektu. Tym razem zajmiemy siÄ™ pobraniem informacji na temat samego obiektu (nazwa klasy, nazwa
modułu itp.). Możemy to zrobić za pomocą funkcji GetTypeData, która zdefiniowana jest następująco:
function GetTypeData(TypeInfo: PTypeInfo): PTypeData;
Funkcję można wywołać np. w ten sposób:
ClassInfo := GetTypeData(btnGet.ClassInfo);
Dzięki temu rekord ClassInfo będzie zawierał informacje na temat komponentu btnGet (typu TButton).
Rekord ClassInfo jest rekordem typu PTypeData, wyglądającym następująco:
PTypeData = ^TTypeData;
TTypeData = packed record
case TTypeKind of
tkUnknown, tkLString, tkWString, tkVariant: ();
tkInteger, tkChar, tkEnumeration, tkSet, tkWChar: (
OrdType: TOrdType;
case TTypeKind of
tkInteger, tkChar, tkEnumeration, tkWChar: (
MinValue: Longint;
MaxValue: Longint;
case TTypeKind of
tkInteger, tkChar, tkWChar: ();
tkEnumeration: (
BaseType: PPTypeInfo;
NameList: ShortStringBase));
tkSet: (
CompType: PPTypeInfo));
tkFloat: (
FloatType: TFloatType);
tkString: (
MaxLength: Byte);
tkClass: (
ClassType: TClass;
ParentInfo: PPTypeInfo;
PropCount: SmallInt;
UnitName: ShortStringBase;
{PropData: TPropData});
tkMethod: (
MethodKind: TMethodKind;
ParamCount: Byte;
ParamList: array[0..1023] of Char
{ParamList: array[1..ParamCount] of
record
Flags: TParamFlags;
10 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
ParamName: ShortString;
TypeName: ShortString;
end;
ResultType: ShortString});
tkInterface: (
IntfParent : PPTypeInfo; { ancestor }
IntfFlags : TIntfFlagsBase;
Guid : TGUID;
IntfUnit : ShortStringBase;
{PropData: TPropData});
tkInt64: (
MinInt64Value, MaxInt64Value: Int64);
end;
Program wykorzystujący powyższy rekord przedstawiony jest na rysunku 14.7. Sam kod zródłowy znajduje się
w listingu 14.2.
Rysunek 14.7. Informacje na temat obiektu TButton
Listing 14.2. Kod zródłowy modułu
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, ValEdit;
type
TMainForm = class(TForm)
btnGet: TButton;
vleValues: TValueListEditor;
procedure btnGetClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses TypInfo;
procedure TMainForm.btnGetClick(Sender: TObject);
var
ClassInfo : PTypeData;
begin
ClassInfo := GetTypeData(btnGet.ClassInfo);
vleValues.InsertRow('Nazwa klasy', ClassInfo.ClassType.ClassName, True);
vleValues.InsertRow('Klasa bazowa', ClassInfo.ClassType.ClassParent.ClassName, True);
vleValues.InsertRow('Moduł', ClassInfo.UnitName, True);
vleValues.InsertRow('Liczba właściwości', IntToStr(ClassInfo.PropCount), True);
vleValues.InsertRow('Liczba parametrów', IntToStr(ClassInfo.ParamCount), True);
vleValues.InsertRow('Rozmiar', IntToStr(btnGet.InstanceSize), True);
end;
end.
Podsumowanie
11 z 12 2009-03-14 15:46
Delphi :: Kompendium :: Rozdział 14 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_14
Niniejszy rozdział poświęcony był bardziej teoretycznym zagadnieniom, związanym z komponentami i z
biblioteką VCL. Przybliżyłem w nim budowę samej biblioteki VCL, wspomniałem też o podstawowych klasach, na
których opiera się owa biblioteka. Nie zapomniałem także o CLX. Praktycznym tworzeniem komponentów w
Delphi zajmiemy siÄ™ w kolejnym rozdziale.
Załączniki:
Listingi_14.zip (7.88 kB)
Więcej informacji
Delphi 2005. Kompendium
programisty
Adam Boduch
Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
« Część III Spis treÅ›ci Tworzenie komponentów
© Helion 2003. Autor: Adam Boduch. Zabrania siÄ™ rozpowszechniania tego tekstu bez zgody autora.
Kategoria: Kompendium
Ostatnia modyfikacja 08-03-2006 14:08 Ostatni autor Adam Boduch
Ilość wyświetleń 16285 Wersja 1
Marooned dnia 04-06-2007 14:28
Pojawiło się mnóstwo '?' w kodzie, warto by go poprawić.
tomalla dnia 28-11-2006 21:03
mam kłopot z funkcją FindComponent.
zrobiłem sobie nową klasę w innym unicie ( jak w tym tekście o klasach w kompendium ).
dodałem wszystkie potrzebne unity w USES - między innymi Classes, a i tak nie rozpoznaje funkcji
FindComponent :?
Undeclared identifier: 'FindComponent'.
Dziwne jest to, że w głównym module ( ten z formami ), ta funkcja jest rozpoznawalna.
POMOCY!!!
Dodaj komentarz
Delphi C/C++
Artykuły Artykuły
Kompendium FAQ
Gotowce C#
Wprowadzenie
FAQ
Assembler
.NET
FAQ
Turbo Pascal
(X)HTML
FAQ
CSS
PHP
JavaScript
FAQ
Z pogranicza
Java
Algorytmy
FAQ
WICEJ
Delphi Programy
C/C++ Dokumentacja
Turbo Pascal Kursy
Assembler Komponenty
PHP WICEJ
12 z 12 2009-03-14 15:46


Wyszukiwarka

Podobne podstrony:
Delphi Kompendium Roz2
Delphi Kompendium Roz10
Delphi Kompendium Roz5
Delphi Kompendium Roz12
Delphi Kompendium Roz8
Delphi Kompendium Roz6
Delphi 2005 Kompendium programisty
Kompendium cz 2
Delphi i PHP
Delphi Leksykon kieszonkowy?lplk
kompendium
Toczenie Kompensacja zaokrÄ…glenia ostrza

więcej podobnych podstron