Własne komponenty
Zanim zaczniesz czytać ten artykuł poczytaj inny potrzebny artykuł klasy.
Jednocześnie podczas nauki będziemy pisać komponent. Będzie to prościutki komponencik sprawdzający, czy jest połączenie z Internetem. Na początek z menu Component wybierz pozycje New Component. W pierwszym polu z listy rozwijalnej musisz wybrać komponent, który będzie bazowym dla naszego, który teraz stworzymy. Wybierz z listy TComponent. Oznacza to, że nasz komponent będzie bazował na klasie TComponent. W kolejnym polu wpisz nazwę komponentu. Wpisz "TIsConnected". W kolejnym polu możesz wybrać miejsce, gdzie ma być zapisany plik z komponentem. Jeszcze z jednej listy wybierz paletę gdzie komponent ma być umieszczony. Wybierz zakładkę Standard. Możesz nacisnąć OK - zostanie wygenerowany plik z modułem, który wygląda tak:
Zwróć uwagę na procedurę Register, które powoduje rejestracje komponentu na palecie komponentów. Przejdźmy do omówienia klasy. Zauważ, że zawiera ona dodatkową pozycje - published. W tej sekcji zamieszczone będą właściwości, które będą dodane do Inspektora Obiektów.
Tak więc jeżeli chcesz umieścić jakąś właściwość w Inspektorze Obiektów to robisz to w sekcji published - np. tak:
Na początek przyzywaczajaj się do specyficznego typu zapisu. W sekcji private zapisuje się zmienne z literą F na początku. W sekcji published właściwość wpisuje się przed słowem property. Najpierw następuje nazwa komponentu, później typ zmiennej. Właściwość może być tylko do odczytu lub do odczytu i do zapisu. Po słowie read zmienna, która ma przypisze wartość do właściwości. Jeżeli chcesz, aby do właściwości można było zapisywać wartości piszesz:
Oczywiście oprócz właściwości możesz zamieszczać w komponentach również zdarzenia - oto przykład ( zdarzenia będą umieszczone na zakładce Events ):
Zdarzenia muszą być typu TNotifyEvent. Dobrze. To na razie ABSOLUTNE podstawy dotyczące pisania komponentów. Te informacje, która zostały tutaj podane wystarczą do napisania prościutkiego komponentu. Klasa wygląda tak:
Kluczową funkcjom w tej klasie jest Connected, która zwraca TRUE jeżeli jest połączenie, a FALSE jeżeli połączenia nie ma. Oto treść tej procedury:
UWAGA. Do listy uses musisz dodać słowo WinInet. Tak więc funkcja sprawdza, czy jest połączenie z netem. Jeżeli tak jest to następuje sprawdzenie, czy zdarzenie OnTrue jest oprogramowane ( Assigned ). Jeżeli tak to zostaje ona wykonana.
I to właściwie całość komponentu. Pozostało jeszcze napisanie konstruktora i destruktora dla komponentu. Nic nadzwyczajnego:
Oto cały kod komponentu:
Na początek możesz niektórych rzecz nie rozumieć. Z czasem jednak się z tym oswoisz. W razie problemów - pisz. Zwróć uwagę na dyrektywę u góry tego komponentu. Na początek można by pomyśleć, że ta dyrektywa włącza zasoby do projektu. Nie mylisz się za bardzo. Ta dyrektywa włącza plik zasobów. ( UWAGA! Dla komponentów zasoby tworzy się także w edytorze graficznym tyle, ze zapisuje się je z rozszerzeniem DCR, a nie RES ). Normalnie Delphi komponent przyozdabia swoją standardową ikoną. Jeżeli chcesz stworzyć własną ikonę to tworzysz BITMAPĘ w edytorze zasobów o rozmiarze 24x24 i rysujesz co zechcesz. Bardzo ważne jest nazewnictwo tej bitmapy. Otóż bitmapa musi mieć nazwę taką jak nazwa klasy tego komponentu! Tzn., że dla komponentu TIsConnected bitmapa w zasobach musi mieć właśnie nazwę TISCONNECTED. Plik z zasobami może mieć obojętnie jaką nazwę.
Dodawanie komponentu do palety nie jest niczym nadzwyczajnym. Z menu Component wybierasz Install Component. Pokaże się okno. W polu Unit File Name musisz wybrać gdzie znajduje się komponent. Teraz naciskasz ok - Delphi skompiluje komponent i doda go do palety komponentów. Wraz z komponentem, który możesz ściągnąć tutaj dostarczony jest program - demo wykorzystujący ten komponent.
Często podczas używania jakiegoś komponentu można napotkać na właściwości, które są wybierane w postaci listy rozwijalnej. Jeżeli chcesz stworyć taką właściwość należy zastosować taką kontukcję:
W tym wypadku stworzyliśmy nową właściwość, nowego typu, która zostanie dodana do inspektora obiektów.
Często również spotykamy się z konstrukcją w postaci drzewa innych właściwości ( dobrym przykładem może być właściwość Font w Inspektorze Obiektów ). Jeżeli chcesz zrobić właśnie taką właściwość to stosujesz taką konstrukcję:
Zwróć uwagę na specyficzny typ kodowania programów. Jeżeli nazwa typu nosi nazwę: TCzescKomputera, to elementy tego typu będą zaczynać się od liter: ck ( od: część komputera ) - np: ckRAM, ckProcesor.
Istnieje możliwość nadawania właściwościom wartości domyślnych, które będą automatycznie umieszczane w Inspektorze Obiektów.
Istnieje możliwość NIE domyślnych. Jeżeli jedna klasa przedstawia się następująco:
Nadaliśmy właśnie właściwości domyślną wartość 100. Gdy teraz umieścimy klasę dziedziczącą z dotychczasowej i chcielibyśmy, aby ta sama właściwość MyProp nie miała już wartości domyślnej. Co robić? Zastosować dyrektywę nodefault:
Komponent, który teraz napiszemy będzie dziedziczny dla komponentu TImage. Jeżeli użytkownik nasunie kursor nad komponent to obrazek zmieni się na jakiś inny - jeżeli się odsunie to powróci do poprzedniego.
Nasz komponent nazywać się będzie TImagePlus...
Jak tworzyć nowy komponent? Już wiesz. Doprowadź klasę nowego komponentu do takiej postaci:
Kluczowym elementem są dwa komunikaty CM_MOUSEENTER i CM_MOUSELEAVE. Dzięki nim możemy kontrolować czas wejścia i wyjścia kursora w obszar komponentu. Cóż po za tym? Dwie właściwości, w których będzie musiała być wpisana ścieżka obrazka. Dodatkowo dwa zdarzenia OnMouseEnter oraz OnMouseLeave, które będą wykonywane w czasie wejścia/wyjścia kursora w obszar komponentu.
Oto treść dwóch komunikatów:
Co robią te procedury? Na początek następuje sprawdzenie, czy wartość FEnter jest uzupełniona. Jeżeli tak to do komponent zostaje załadowany obrazek. Następnie następuje sprawdzenie, czy wygenerowane jest zdarzenie FOnEnter - jeżeli tak to jest ona wykonywana. Tak samo ( podobnie ) ma się sprawa z drugą procedurą. To właściwie wszystko co związane z komponentem! Oto kod całego komponentu:
Jednocześnie podczas nauki będziemy pisać komponent. Będzie to prościutki komponencik sprawdzający, czy jest połączenie z Internetem. Na początek z menu Component wybierz pozycje New Component. W pierwszym polu z listy rozwijalnej musisz wybrać komponent, który będzie bazowym dla naszego, który teraz stworzymy. Wybierz z listy TComponent. Oznacza to, że nasz komponent będzie bazował na klasie TComponent. W kolejnym polu wpisz nazwę komponentu. Wpisz "TIsConnected". W kolejnym polu możesz wybrać miejsce, gdzie ma być zapisany plik z komponentem. Jeszcze z jednej listy wybierz paletę gdzie komponent ma być umieszczony. Wybierz zakładkę Standard. Możesz nacisnąć OK - zostanie wygenerowany plik z modułem, który wygląda tak:
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type TIsConnected = class(TComponent) private { Private declarations } protected { Protected declarations } public { Public declarations } published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Standard', [TIsConnected]); end; end.
Zwróć uwagę na procedurę Register, które powoduje rejestracje komponentu na palecie komponentów. Przejdźmy do omówienia klasy. Zauważ, że zawiera ona dodatkową pozycje - published. W tej sekcji zamieszczone będą właściwości, które będą dodane do Inspektora Obiektów.
Tak więc jeżeli chcesz umieścić jakąś właściwość w Inspektorze Obiektów to robisz to w sekcji published - np. tak:
private FText : String; protected { Protected declarations } public { Public declarations } published property Text : String read FText;
Na początek przyzywaczajaj się do specyficznego typu zapisu. W sekcji private zapisuje się zmienne z literą F na początku. W sekcji published właściwość wpisuje się przed słowem property. Najpierw następuje nazwa komponentu, później typ zmiennej. Właściwość może być tylko do odczytu lub do odczytu i do zapisu. Po słowie read zmienna, która ma przypisze wartość do właściwości. Jeżeli chcesz, aby do właściwości można było zapisywać wartości piszesz:
property Text : String read FText write FText;
Oczywiście oprócz właściwości możesz zamieszczać w komponentach również zdarzenia - oto przykład ( zdarzenia będą umieszczone na zakładce Events ):
TIsConnected = class(TComponent) private FTrue, FFalse : TNotifyEvent; public { ... } published property OnTrue : TNotifyEvent read FTrue write FTrue; property OnFalse : TNotifyEvent read FFalse write FFalse; end;
Zdarzenia muszą być typu TNotifyEvent. Dobrze. To na razie ABSOLUTNE podstawy dotyczące pisania komponentów. Te informacje, która zostały tutaj podane wystarczą do napisania prościutkiego komponentu. Klasa wygląda tak:
type TIsConnected = class(TComponent) private FTrue, FFalse : TNotifyEvent; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; function Connected : Boolean; // sprawdza, czy jest polaczenie z netem procedure ShowAbout; // wyswietla informacje o autorze... published property OnTrue : TNotifyEvent read FTrue write FTrue; property OnFalse : TNotifyEvent read FFalse write FFalse; end;
Kluczową funkcjom w tej klasie jest Connected, która zwraca TRUE jeżeli jest połączenie, a FALSE jeżeli połączenia nie ma. Oto treść tej procedury:
function TIsConnected.Connected: Boolean; var Flags: DWORD; begin Flags := INTERNET_CONNECTION_MODEM or INTERNET_CONNECTION_LAN or INTERNET_CONNECTION_PROXY or INTERNET_CONNECTION_MODEM_BUSY; Result := InternetGetConnectedState(@Flags, 0); // sprawdz polaczenie. if Result then begin { jezeli procedura OnTrue jest wygenerowana uruchom ja } if Assigned(FTrue) then OnTrue(Self); end else if Assigned(FFalse) then OnFalse(Self); end;
UWAGA. Do listy uses musisz dodać słowo WinInet. Tak więc funkcja sprawdza, czy jest połączenie z netem. Jeżeli tak jest to następuje sprawdzenie, czy zdarzenie OnTrue jest oprogramowane ( Assigned ). Jeżeli tak to zostaje ona wykonana.
I to właściwie całość komponentu. Pozostało jeszcze napisanie konstruktora i destruktora dla komponentu. Nic nadzwyczajnego:
constructor TIsConnected.Create(AOwner: TComponent); begin inherited Create(AOwner); end; destructor TIsConnected.Destroy; begin inherited Destroy; end;
Oto cały kod komponentu:
{---------------------------------------------------------------} { IsConnected v. 1.0 [03.06.2001] } { Copyright (c) 2001 by Adam Boduch } { http://4programmers.net } { boduch@poland.com } {---------------------------------------------------------------} unit IsConnected; interface uses Windows, Classes; {$R COMPONENTRES.DCR} type TIsConnected = class(TComponent) private FTrue, FFalse : TNotifyEvent; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; function Connected : Boolean; // sprawdza, czy jest polaczenie z netem procedure ShowAbout; // wyswietla informacje o autorze... published property OnTrue : TNotifyEvent read FTrue write FTrue; property OnFalse : TNotifyEvent read FFalse write FFalse; end; procedure Register; implementation uses WinInet; procedure Register; begin RegisterComponents('Standard', [TIsConnected]); end; procedure TIsConnected.ShowAbout; begin { wyswietl informacje o autorze } MessageBox(0, 'TIsConnected' + #13#13+ 'Copyright (c) 2001 by Adam Boduch ' + #13+ 'http://4programmers.net'+#13+ 'boduch@poland.com', '', MB_OK); end; function TIsConnected.Connected: Boolean; var Flags: DWORD; begin Flags := INTERNET_CONNECTION_MODEM or INTERNET_CONNECTION_LAN or INTERNET_CONNECTION_PROXY or INTERNET_CONNECTION_MODEM_BUSY; Result := InternetGetConnectedState(@Flags, 0); // sprawdz polaczenie. if Result then begin { jezeli procedura OnTrue jest wygenerowana uruchom ja } if Assigned(FTrue) then OnTrue(Self); end else if Assigned(FFalse) then OnFalse(Self); end; constructor TIsConnected.Create(AOwner: TComponent); begin inherited Create(AOwner); end; destructor TIsConnected.Destroy; begin inherited Destroy; end; end.
Na początek możesz niektórych rzecz nie rozumieć. Z czasem jednak się z tym oswoisz. W razie problemów - pisz. Zwróć uwagę na dyrektywę u góry tego komponentu. Na początek można by pomyśleć, że ta dyrektywa włącza zasoby do projektu. Nie mylisz się za bardzo. Ta dyrektywa włącza plik zasobów. ( UWAGA! Dla komponentów zasoby tworzy się także w edytorze graficznym tyle, ze zapisuje się je z rozszerzeniem DCR, a nie RES ). Normalnie Delphi komponent przyozdabia swoją standardową ikoną. Jeżeli chcesz stworzyć własną ikonę to tworzysz BITMAPĘ w edytorze zasobów o rozmiarze 24x24 i rysujesz co zechcesz. Bardzo ważne jest nazewnictwo tej bitmapy. Otóż bitmapa musi mieć nazwę taką jak nazwa klasy tego komponentu! Tzn., że dla komponentu TIsConnected bitmapa w zasobach musi mieć właśnie nazwę TISCONNECTED. Plik z zasobami może mieć obojętnie jaką nazwę.
Dodawanie komponentu do palety
Dodawanie komponentu do palety nie jest niczym nadzwyczajnym. Z menu Component wybierasz Install Component. Pokaże się okno. W polu Unit File Name musisz wybrać gdzie znajduje się komponent. Teraz naciskasz ok - Delphi skompiluje komponent i doda go do palety komponentów. Wraz z komponentem, który możesz ściągnąć tutaj dostarczony jest program - demo wykorzystujący ten komponent.
Pisanie komponentu cd.
Często podczas używania jakiegoś komponentu można napotkać na właściwości, które są wybierane w postaci listy rozwijalnej. Jeżeli chcesz stworyć taką właściwość należy zastosować taką kontukcję:
type TCars = (tcFord, tcFiat, tcBMW); TTest = class(TAbstractSocket) private FCar : TCars; protected { Protected declarations } public { Public declarations } published property Car : TCars read FCar write FCar; end;
W tym wypadku stworzyliśmy nową właściwość, nowego typu, która zostanie dodana do inspektora obiektów.
Często również spotykamy się z konstrukcją w postaci drzewa innych właściwości ( dobrym przykładem może być właściwość Font w Inspektorze Obiektów ). Jeżeli chcesz zrobić właśnie taką właściwość to stosujesz taką konstrukcję:
type TSetCars = (tcFord, tcFiat, tcBMW); TCars = set of TSetCars; TTest = class(TComponent) private FCar : TCars; protected { Protected declarations } public { Public declarations } published property Car : TCars read FCar write FCar; end;
Zwróć uwagę na specyficzny typ kodowania programów. Jeżeli nazwa typu nosi nazwę: TCzescKomputera, to elementy tego typu będą zaczynać się od liter: ck ( od: część komputera ) - np: ckRAM, ckProcesor.
Wartości domyślne
Istnieje możliwość nadawania właściwościom wartości domyślnych, które będą automatycznie umieszczane w Inspektorze Obiektów.
published property Text : String default 'Adam';
Istnieje możliwość NIE domyślnych. Jeżeli jedna klasa przedstawia się następująco:
TFirst = class {...} published property MyProp : Integer default 100; end;
Nadaliśmy właśnie właściwości domyślną wartość 100. Gdy teraz umieścimy klasę dziedziczącą z dotychczasowej i chcielibyśmy, aby ta sama właściwość MyProp nie miała już wartości domyślnej. Co robić? Zastosować dyrektywę nodefault:
TSecond = class(TFirst) { ... } published property MyProp : Integer nodefault; end;
Piszemy kolejny komponent...
Komponent, który teraz napiszemy będzie dziedziczny dla komponentu TImage. Jeżeli użytkownik nasunie kursor nad komponent to obrazek zmieni się na jakiś inny - jeżeli się odsunie to powróci do poprzedniego.
Nasz komponent nazywać się będzie TImagePlus...
Jak tworzyć nowy komponent? Już wiesz. Doprowadź klasę nowego komponentu do takiej postaci:
type TImagePlus = class(TImage) private FEnter, FLeave : String; FOnEnter, FOnLeave : TNotifyEvent; protected { komunikaty realizujace wejscie i wyjscie kursora w obszar komponentu } procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published { wlasciwosci, ktore beda dodane do Inspektora Obiektow } property EnterImage : String read FEnter write FEnter; property LeaveImage : String read FLeave write FLeave; { zdarzenia dla komponentu. Mozemy dodatkowo kontrolowac wejscie i wyjscie kursora w obszar komponentu } property OnMouseEnter : TNotifyEvent read FOnEnter write FOnEnter; property OnMouseLeave : TNotifyEvent read FOnLeave write FOnLeave; end;
Kluczowym elementem są dwa komunikaty CM_MOUSEENTER i CM_MOUSELEAVE. Dzięki nim możemy kontrolować czas wejścia i wyjścia kursora w obszar komponentu. Cóż po za tym? Dwie właściwości, w których będzie musiała być wpisana ścieżka obrazka. Dodatkowo dwa zdarzenia OnMouseEnter oraz OnMouseLeave, które będą wykonywane w czasie wejścia/wyjścia kursora w obszar komponentu.
Oto treść dwóch komunikatów:
procedure TImagePlus.CMMouseEnter(var Msg: TMessage); begin // laduje do komponentu obrazek ze zmiennej if FEnter <> '' then Picture.LoadFromFile(FEnter); { jezeli wlasciwosc OnMouseEnter jest oprogramowana uruchom ja } if Assigned(FOnEnter) then OnMouseEnter(Self); Msg.Result := 1; end; procedure TImagePlus.CMMouseLeave(var Msg: TMessage); begin if FLeave <> '' then Picture.LoadFromFile(FLeave); // zaladuj bitmape { jezeli wlasciwosc OnMouseLeave jest oprogramowana uruchom ja } if Assigned(FOnLeave) then OnMouseLeave(Self); Msg.Result := 1; end;
Co robią te procedury? Na początek następuje sprawdzenie, czy wartość FEnter jest uzupełniona. Jeżeli tak to do komponent zostaje załadowany obrazek. Następnie następuje sprawdzenie, czy wygenerowane jest zdarzenie FOnEnter - jeżeli tak to jest ona wykonywana. Tak samo ( podobnie ) ma się sprawa z drugą procedurą. To właściwie wszystko co związane z komponentem! Oto kod całego komponentu:
(****************************************************************) (* *) (* TImagePlus v. 1.0.0 *) (* Copyright (c) 2001 by Service for programmers *) (* Adam Boduch; e-mail: boduch@poland.com *) (* http://4programmers.net *) (* 03.06.2001 r. *) (* *) (****************************************************************) unit ImagePlus; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls; type TImagePlus = class(TImage) private FEnter, FLeave : String; FOnEnter, FOnLeave : TNotifyEvent; protected { komunikaty realizujace wejscie i wyjscie kursora w obszar komponentu } procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published { wlasciwosci, ktore beda dodane do Inspektora Obiektow } property EnterImage : String read FEnter write FEnter; property LeaveImage : String read FLeave write FLeave; { zdarzenia dla komponentu. Mozemy dodatkowo kontrolowac wejscie i wyjscie kursora w obszar komponentu } property OnMouseEnter : TNotifyEvent read FOnEnter write FOnEnter; property OnMouseLeave : TNotifyEvent read FOnLeave write FOnLeave; end; procedure Register; implementation procedure Register; begin RegisterComponents('Win32', [TImagePlus]); // rejestracja komponentu end; procedure TImagePlus.CMMouseEnter(var Msg: TMessage); begin // laduje do komponentu obrazek ze zmiennej if FEnter <> '' then Picture.LoadFromFile(FEnter); { jezeli wlasciwosc OnMouseEnter jest oprogramowana uruchom ja } if Assigned(FOnEnter) then OnMouseEnter(Self); Msg.Result := 1; end; procedure TImagePlus.CMMouseLeave(var Msg: TMessage); begin if FLeave <> '' then Picture.LoadFromFile(FLeave); // zaladuj bitmape { jezeli wlasciwosc OnMouseLeave jest oprogramowana uruchom ja } if Assigned(FOnLeave) then OnMouseLeave(Self); Msg.Result := 1; end; constructor TImagePlus.Create(AOwner: TComponent); begin inherited Create(AOwner); end; destructor TImagePlus.Destroy; begin inherited Destroy; end; end.
5 komentarzy
A co jeśli chcemy do Eventu dodac jakieś zmienne? Np.
procedure OnKlick(Sender:TObject; X,Y:Integer);
?
owe "powiązanie" nazywa się dziedziczeniem i następuje w tym miejscu : TImagePlus = class(TImage) co onacza, że komponent TimagePlus dziedziczy (przejmuje) właściwości i zdarzenia od komponentu TImage.
A w jaki sposób można powiązać swój komponent z jakimś komponentem (na przykład typu TWebBrowser)?
Wiem że od ostatniego komentarza mineło sporo czasu, ale dopiero teraz przeglądam sobie ten artykul i
odpowiem, że przykład jak skonstruuować zdarzenie dla komponnetu z jakimiś zmiennymi opisano tutaj
http://4programmers.net/Delphi[...]pisać_komponent_i_wysłać_pinga - wystarczy przejrzeć kod źródlowy.
Jest w tym komponencie zdarzenie OnReply ktore posiada dodatkowe zmienne. To jakby ktos też szukał.