Tworzenie komponentów
Tworząc programy w środowisku Borland C++ Builder niemal zawsze korzystamy z gotowych komponentów, jest to bardzo wygodne ponieważ umożliwia szybkie tworzenie programów. Zamiast tworzyć nową klasę lub dołączać już istniejące i ręcznie deklarować właściwości, wystarczy za pomocą myszki umieścić komponent na formularzu, no ale o tym wszyscy wiedzą. Nie wszyscy natomiast potrafią tworzyć własne komponenty chociaż jest to w zasadzie bardzo proste i właśnie temu poświęcony jest ten dział.
Stworzymy komponent, który będzie zmodyfikowanym komponentem TLabel a będzie mianowicie "drukował" tekst zamiast wyświetlać go w całości, przy czym nie chodzi tutaj o drukowanie tekstu na drukarce, lecz po prostu o wyświetlanie go litera po
literze:
![]()
1. Tworzenie komponentu TCBLabel.
Tworzymy na dysku folder w którym zapiszemy nasz projekt i uruchamiamy BCB. W menu File wybieramy Close All, żeby zamknąć formularz utworzony przy uruchamianiu programu. Następnie z menu Components wybieramy New Component wyskoczy okno dialogowe umożliwiające zdefiniowanie tworzonego przez nas komponentu:

W okienku Ancestor type: wybieramy klasę od której będzie wywodził się komponent, w tym przypadku wybieramy tam TLabel.
W okienku Class name: wpisujemy nazwę klasy jaką będzie nosił nasz komponent, w tym przypadku wpisujemy TCBLabel (nazwa może być dowolna, lecz dla porządku zawsze powinna być poprzedzona dużą literą T).
W okienku Palette Page: wybieramy lub wpisujemy nazwę palety na której zostanie umieszczony nasz komponent, ja użyłem palety CyfBar (można użyć już istniejącej nazwy palety, lub stworzyć własną).
Pozostałe okienka zostawiamy tak jak są. Następnie zapisujemy wszystko w wybranym katalogu ja użyłem nazwy CBLabel. Program utworzy pliki źródłowy i nagłówkowy zawierające już wszystkie niezbędne elementy potrzebne do utworzenia komponentu.
Przechodzimy do pliku nagłówkowego (CBLabel.h),
znajdują się tam cztery sekcje:
private: - tutaj deklarujemy zmienne i funkcje, które będą dostępne tylko wewnątrz klasy (w tym przypadku klasa TCBLabel),
protected: - tutaj deklarujemy zmienne i funkcje chronione, czyli takie które są dostępne dla tej klasy i klas od niej wyprowadzanych, jest to przydatne tylko wtedy gdy byśmy chcieli w przyszłości utworzyć np. nową klasę, która wywodziła by się od tej klasy i miałaby dziedziczyć zmienne i funkcje w niej zawarte. W tym przykładzie nie będziemy z tej sekcji korzystać,
public: - tutaj deklarujemy zmienne i funkcje, które będą dostępne wewnątrz klasy jak również poza nią,
__published: - tutaj definiujemy właściwości komponentu, które będą widoczne na zakładce Properties w inspektorze obiektów (Object Inspector).
W sekcji public: powinien znajdować się już konstruktor klasy:
| __fastcall TCBLabel(TComponent* Owner); |
Każda klasa powinna zawsze posiadać konstruktora i destruktora klasy, dlatego dołączymy do sekcji public: pliku nagłówkowego destruktor klasy:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- public: __fastcall TCBLabel(TComponent* Owner); __fastcall ~TCBLabel(void); // destruktor //-------------------------------- |
Zadeklarowany w pliku nagłówkowym konstruktor klasy powinien posiadać już definicję w pliku źródłowym (CBLabel.cpp), natomiast definicję destruktora musimy sami dołączyć. W tym celu przechodzimy do pliku źródłowego i umieszczamy następujący kod:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::~TCBLabel(void) { } //-------------------------------- |
Wracamy do pliku nagłówkowego i w sekcji private: deklarujemy zmienną typu AnsiString - AText, która będzie pobierała tekst do drukowania, oprócz tej zmiennej potrzebna będzie również funkcja pobierająca tekst - SetAText. Natomiast w sekcji __published zadeklarujemy właściowści komponentu o nazwie Text. Te trzy elementy będą ze sobą ściśle powiązane:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- class PACKAGE TCBLabel : public TLabel { private: AnsiString AText; void __fastcall SetAText(String tekst); protected: public: __fastcall TCBLabel(TComponent* Owner); __fastcall ~TCBLabel(void); __published: __property AnsiString Text = {read = AText, write = SetAText}; }; //-------------------------------- |
Przechodzimy do pliku źródłowego i definiujemy funkcję SetAText:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- void __fastcall TCBLabel::SetAText(String tekst) { AText = tekst; } //-------------------------------- |
Ten komponent nie potrafi jeszcze robić nie więcej niż to co robi standardowy komponent TLabel, ale gdybyśmy go teraz zainstalowali to na zakładce Properties byłaby widoczna właściwość Text. W chwili uruchomienia komponentu, właściwość Text nie zawiera żadnych właściwości, ale można to zmienić przypisując jej właściwość domyślną, a należy to zrobić wewnątrz konstruktora klasy:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::TCBLabel(TComponent* Owner) : TLabel(Owner) { AText = "http://cyfbar.republika.pl/"; } //-------------------------------- |
...czyli taka tam sobie wartość.
Do drukowania tekstu potrzebna nam będzie klasa TTimer, dlatego w sekcji private: pliku nagłówkowego deklarujemy obiekt Timer:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- private: AnsiString AText; void __fastcall SetAText(String tekst); TTimer *Timer; //-------------------------------- |
Nowy obiekt Timer zostanie utworzony wewnątrz konstruktora klasy:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::TCBLabel(TComponent* Owner) : TLabel(Owner) { AText = "http://cyfbar.republika.pl/"; Timer = new TTimer(NULL); } //-------------------------------- |
Wewnątrz destruktora zniszczymy utworzony obiekt Timer, a to po to żeby usuwać to co nie jest nam już potrzebne, a skoro destruktor klasy usuwa klasę z pamięci to powinien również usuwać wszystkie obiekty utworzone wewnątrz:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::~TCBLabel(void) { delete Timer; } //-------------------------------- |
Szybkość z jaką ma być drukowany tekst będzie zależała od częstotliwości "tykania" obiektu Timer, każdy obiekt Timer posiada właściwość Interval, którą można dowolnie zmieniać i dlatego dodamy ją do właściwości naszego komponentu. W tym celu w sekcji private: deklarujemy zmienną typu int - AInterval, oraz funkcją SetAInterval - która będzie pobierała częstotliwość z jaką ma pracować obiekt Timer, natomiast w sekcji __published: zadeklarujemy właściwość Interval, która będzie właściwością naszego komponentu:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- class PACKAGE TCBLabel : public TLabel { private: AnsiString AText; void __fastcall SetAText(String tekst); TTimer *Timer; int AInterval; void __fastcall SetAInterval(int a); protected: public: __fastcall TCBLabel(TComponent* Owner); __fastcall ~TCBLabel(void); __published: __property AnsiString Text = {read = AText, write = SetAText}; __property int Interval = {read = AInterval, write = SetAInterval}; }; //-------------------------------- |
Przechodzimy do pliku źródłowego i definiujemy funkcję SetAInterval:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- void __fastcall TCBLabel::SetAInterval(int a) { AInterval = a; Timer->Interval = AInterval; } //-------------------------------- |
Wewnątrz konstruktora ustawiamy początkowe parametry pracy obiektu Timer:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::TCBLabel(TComponent* Owner) : TLabel(Owner) { AText = "http://cyfbar.republika.pl/"; AInterval = 500; Timer = new TTimer(NULL); Timer->Enabled = false; Timer->Interval = Interval; } //-------------------------------- |
Każdy obiekt Timer posiada zdarzenie OnTimer, więc wykorzystamy je do tego by drukować tekst, dlatego w sekcji private: pliku nagłówkowego deklarujemy funkcję TimerTimer oraz zmienną typu int - t, która to zmienna zostanie wykorzystana wewnątrz zdarzenia OnTimer:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- private: AnsiString AText; void __fastcall SetAText(String tekst); TTimer *Timer; int AInterval; void __fastcall SetAInterval(int a); void __fastcall TimerTimer(TObject *Sender); int t; //-------------------------------- |
Funkcję TimerTimer łączymy ze zdarzeniem OnTimer, obiektu Timer wewnątrz konstruktora klasy. Ustawiamy również początkową wartość zmiennej t:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- __fastcall TCBLabel::TCBLabel(TComponent* Owner) : TLabel(Owner) { AText = "http://cyfbar.republika.pl/"; AInterval = 500; Timer = new TTimer(NULL); Timer->Enabled = false; Timer->Interval = Interval; Timer->OnTimer = TimerTimer; t = 0; } //-------------------------------- |
Następnie definiujemy funkcję TimerTimer i umieszczamy wewnątrz niej kod odpowiedzialny za drukowanie tekstu:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- void __fastcall TCBLabel::TimerTimer(TObject *Sender) { t++; if(t <= AText.Length()) Caption = Text.SubString(1, t); if(t > AText.Length()) { Timer->Enabled = false; t = 0; } } //-------------------------------- |
Komponent już prawie działa, ale jeszcze wewnątrz sekcji public: pliku nagłówkowego deklarujemy funkcję Execute, która posłuży do inicjowania drukowania tekstu:
| // Plik nagłówkowy np. CBLabel.h. //-------------------------------- public: __fastcall TCBLabel(TComponent* Owner); __fastcall ~TCBLabel(void); void __fastcall Execute(void); //-------------------------------- |
Następnie w pliku źródłowym definiujemy funkcję Execute:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- void __fastcall TCBLabel::Execute(void) { Timer->Enabled = true; } //-------------------------------- |
Plik źródłowy: CBLabel.cpp
Plik nagłówkowy: CBLabel.h
2. Testowanie komponentu TCBLabel.
Nasz komponent jest prawie gotowy, jednak zanim go zainstalujemy dobrze byłoby go przetestować, żeby sprawdzić i ewentualnie poprawić błędy. W tym celu tworzymy nowy projekt i zapisujemy go pod dowolną nazwą w oddzielnym folderze (innym niż ten w którym znajduje się komponent). Następnie w menu Project wybieramy Add to Project... i dołączamy w ten sposób do projektu plik CBLabel.cpp. Przechodzimy do pliku nagłówkowego (np. Unit1.h) i w sekcji include umieszczamy kod dołączający plik CBLabel.h:
| // Plik nagłówkowy np. Unit1.h. //-------------------------------- #ifndef Unit1H #define Unit1H //-------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include "CBLabel.h"// <-- //-------------------------------- |
Następnie w sekcji private pliku nagłówkowego deklarujemy nowy obiekt typu TCBLabel:
| // Plik nagłówkowy np. Unit1.h. //-------------------------------- private: TCBLabel *Label1; //-------------------------------- |
Przechodzimy do pliku źródłowego i inicjujemy obiekt Label1 typu TCBLabel:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" //-------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //-------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Label1 = new TCBLabel(this); Label1->Parent = this; Label1->Left = 10; Label1->Top = 20; Label1->AutoSize = false; Label1->Width = 200; Label1->Interval = 50; Label1->Text = "Przykładowy tekst" } //-------------------------------- |
Teraz umieszczamy na formularzy przycisk Button1 i w zdarzeniu OnClick wywołujemy metodę Execute obiektu Label1:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { Label1->Execute(); } //-------------------------------- |
Kompilujemy i uruchamiamy program, jeśli wszystko zostało zrobione dobrze to komponent powinien działać.
3. Instalowanie komponentu TCBLabel.
W zasadzie komponent TCBLabel jest prawie gotowy do zainstalowania, jednak jeżeli zrobimy to teraz to na palecie komponentów będzie on reprezentowany przez taką samą ikonę jaką posiada komponent TLabel. Żeby to zmienić uruchamiamy program Image Editor wchodzący w skład pakietu BCB i w menu File wybieramy New | Resource File (.res). W okienku które wyskoczy klikamy prawym klawiszem myszy na opcji -Contents i z menu kontekstowego wybieramy New | Bitmap. Rozmiar bitmapy ustawiamy na 32x32 w 256 kolorach. Następnie zmieniamy nazwę Bitmap1 na nazwę klasy naszego komponentu, czyli na TCBLabel, klikamy dwukrotnie na
etykiecie TCBLabel i w oknie które wyskoczy możemy utworzyć własną ikonę komponentu. Po utworzeniu ikony zamykamy okno edycji i zapisujemy plik pod taką nazwą jaką nosi nasz plik z komponentem, czyli w tym przypadku pod nazwą CBLabel.
Jeszcze trzeba tylko w pliku CBLabel.cpp umieścić kod dołączający plik CBLabel.res do projektu:
| // Plik źródłowy np. CBLabel.cpp //-------------------------------- #include <vcl.h> #pragma hdrstop #include "CBLabel.h" #pragma resource "*.res"// <-- #pragma package(smart_init) //-------------------------------- |
Teraz samo instalowanie komponentu jest zależne od wersji BCB, ale we wszystkich wersjach powyżej 1 powinno się to odbywać za pomocą "paczek". Zamykamy wszystkie otwarte okna w BCB i w menu File wybieramy New..., wyskoczy okno New Item w którym wybieramy Package. Pojawi się nowe okno w którym wybieramy opcję Add, wyskoczy nowe okno w którym klikamy na przycisku Browse... i wybieramy nasz plik z komponentem - CBLabel.cpp, klikamy na OK a następnie zapisujemy projekt Package1.cpp w katalogu w którym znajduje się nasz komponent np. pod nazwą PCBLabel, kompilujemy wszystko i jeżeli nie wyskoczą żadne komunikaty o błędach to wybieramy przycisk Install i komponent powinien zostać w ten sposób zainstalowany. Na koniec zapisujemy wszystko jeszcze raz i komponent jest już gotowy do użycia.
W dziale download - komponenty znajduje się wersja instalacyjna komponentu dla BCB 4.
Opracował: Cyfrowy Baron