Delphi komponentleri yazabilmek için Delphi
programlama dilinin komut setine geniş ölçüde hakim olmak ve özellikle OOP
(Nesneye Dayalı Programlama) hakkında yeterli bilgiye sahip olmak gereklidir.
Eğer kendinizi komponent yazma konusuna hazır hissediyorsanız,
haydi ilk komponentimizi yazmak için hazırlıklara başlayalım.
Komponent yazmaya başlamadan önce, karar vermeniz gereken bir kaç nokta var. Bunlardan
bazıları:
-
Yazdığınız komponent hangi tür programlarda kullanılabilecek?
(Genel bir komponent mi yoksa sadece bir programa özel bir komponent mi?)
-
Yazdığınız komponent, standard bir komponentten türetilecekse, bu
komponent yeni özellik ve olaylara sahip olacak mı?
-
Yazdığınız komponent görsel bir komponent mi (Label, Edit vs), yoksa görsel olmayan
bir komponent mi (TQuery, TTable vs.) olacak?
-
Komponentiniz olay güdümlü özelliklere (event driven) sahip olacak mı?
Görüldüğü gibi komponent yazmaya başlamadan önce karar verilmesi
gereken soruların cevapları hazır olmalıdır. Bu sorular daha artırılabilir. Onun
için önce çok iyi bir planlama, gerekiyorsa sınıfların organizeli olarak
planlanması gerekir.
Biz burada görsel olmayan ve en temel özelliklere sahip bir
komponent yazmak için gereken özellikleri göstereceğiz. Bu dökümanda aşağıdaki
konular anlatılacaktır:
-
VCL (Görsel komponent Kütüphanesi) içerisinde var olan bir
komponenti, tüm özelliklerini
devralarak yeni bir komponent oluşturacağız.
-
VCL içerisinde
bulunmayan ve görsel olmayan, başlangıç ve bitiş zamanları arasındaki süreyi
veren bir komponent yazacağız.
-
Oluşturduğumuz yeni komponentimizden
türeteceğimiz ve sadece çıktı formatını değiştirmek için yeni bir komponent
yazacağız.
-
Oluşturduğumuz ikinci komponentten yine bir komponent türeterek bu yeni
komponente olaylar ekleyeceğiz.
VCL'den komponent türetme
VCL içerinde bulunan bir komponentin tüm özelliklerine sahip
yeni bir komponent oluşturmak için Delphi Menüsünden "komponent/New
Component"
komutunu verin. Biz TLabel sınıfından yeni bir komponent oluşturup adını
"TGokLabel"
koyacağız.(Delphi'de kullanılan tüm sınıfların başında her zaman "T"
olması gerektiğini unutmayın.)
"Ancestor Type" Kısmına "TLabel" yazın.
Ancestor Type ile özellikleri devralınacak sınıfın tip tanımı kastedilmektedir.
"Class Name" alanına ise
oluşturacağımız komponent için seçtiğimiz ismi yazmalıyız. Bizim komponentimizin
adı "TGokLabel" olacaktır.
"Palette Page" alanına komponentimizin, Delphi
komponent
paletinin hangi kısmında gösterilmesini istiyorsanız onun adını yazınız. (Örn :
"Samples")
"Unit File Name" alanına yeni komponentimiz için oluşturulacak olan
".PAS" dosyasının dizinini ve dosyanın adını yazınız. (Unit adı ile komponent
adının aynı olmasına dikkat ediniz. Örn: "C:\mycomp\GokLabel.pas")
Gerekli Bilgileri girdikten sonra "New Component" penceresinin görünümü Şekil 1
deki gibi olmalıdır.
Şimdi "Install" butonuna basın ve Komponentin hangi kütüphaneye
ekleneceğiniz soran pencerede "Into New Package" kısmını seçip "File Name"
kısmına "c:\mycomp\mycomp.dpk" (C:\mycomp dizininin var olduğunu
varsayıyoruz. Eğer başka bir dizin ve dosya adı belirtmek isterseniz "Browse"
butonuna basarak dizin ve dosya seçebilirsiniz.) ve "Description" kısmına yeni
paket ile ilgili kısa bir açıklama yazın. Örnek için Şekil 2'ye bakınız.
"OK" butonuna bastığınız zaman Delphi yeni bir unit oluşturacak
ve "mycomp.dpk" adlı paketi derleyecektir. Şimdi komponent paletinin "Samples"
kısmında üzerinde "A" harfi bulunan "GokLabel" isimli bir
komponent görebilirsiniz
ve bu komponentin "Label" komponentinden hiç bir farkı yoktur.
Şimdi GokLabel isimli komponentimize yeni bir özellik olarak
Copyright Bilgisi ekleyelim. Bunun için (eğer açık değilse) "GokLabel.pas"
dosyasını açın ve Type bloğunu aşağıdaki gibi değiştirin.
type
TGokLabel = class(TLabel)
private
{ Private declarations }
FCopyright : String;
protected
{ Protected declarations }
Function GetCopyright : String;
public
{ Public declarations }
published
{ Published declarations }
Property Copyright : String read GetCopyright Write FCopyRight;
end;
procedure Register;
Görüldüğü gibi private deklarasyonu içerisinde FCopyRight adlı
bir ön değişken, protected alanında String bir değer üreten GetCopyright
fonksiyonu ve Published alanında Copyright adlı bir özellik tanımlıyor ve
Copyright özelliğinin değerini GetCopyrgiht adlı fonksiyondan alacağını ve
FCopyright değişkenine aktaracağını belirliyoruz.
Şimdi de "GokLabel.pas" unitimizin implemantation kısmına
aşağıdaki kodu ekleyin:
Function TGokLabel.GetCopyright:String;
begin
Result := 'Copyright by Mustafa GOKMEN, Konya, 2002';
end;
GetCopyright fonksiyonu her zaman aynı sonucu döndürür. Bu
nedenle programcı Copyright özelliğinin değerini kod ile değiştirmeye çalışsa
bile Copyright özelliğinin değeri aynı kalacak ve sabit bir değer
döndürecektir.
Implemantation kısmında, komponent yazma ile uğraşırken her
zaman göreceğiniz bir prosedür daha var.
procedure Register;
begin
RegisterComponents('Samples', [TGokLabel]);
end;
Register adlı bu prosedür, sınıfımızı VCL Kütüphanesine
kaydettirir ve komponentin hangi komponent paletinde gösterileceğini belirtir.
Örneğimizde TGokLabel Sınıfı komponent paletinde "Samples" adlı grubu altına
kaydettirilmektedir. Eğer bir unit içerisinde birden fazla komponent (sınıf)
tanımlamışsanız, Register prosedürü altında bunları da VCL kütüphanesine
kaydettirmeniz gereklidir.
"GokLabel.pas" dosyasının son hali aşağıdaki gibi olmalıdır.
unit GokLabel;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, StdCtrls;
type
TGokLabel = class(TLabel)
private
{ Private declarations }
FCopyright : String;
protected
{ Protected declarations }
Function GetCopyright : String;
public
{ Public declarations }
published
{ Published declarations }
Property Copyright : String read GetCopyright Write FCopyRight;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TGokLabel]);
end;
Function TGokLabel.GetCopyright:String;
begin
Result := 'Copyright by Mustafa GOKMEN, Konya, 2002';
end;
end.
Şimdi, "GokLabel.pas" dosyasını kaydedin ve "mycomp.dpk" dosyasını tekrar
derleyip onu da kaydedin. Yeni bir proje başlatıp Form üzerine "GokLabel"
komponentinden bir tane indirdiğiniz zaman, Object Inspector'da Copyright alanını
ve bu bilginin değiştirilemez olduğunu göreceksiniz. Şekil 3'te Object
Inspector'daki Copyright alanını görebilirsiniz.
Yeni komponentimiz
Şimdi kendimize ait olan ve VCL içerisinde bulunmayan bir komponent
yazalım. TLabel komponentinden bir başka komponent olarak TGokLabel komponentini
oluştururken Type bloğu altında dört ayrı kısım vardı ve biz bunlardan
bahsetmemiştik. Bunlar OOP ile ilgili Private, Protected, Public ve Published
kısımlarıdır.Şimdi bunları kısaca hatırlayalım.
|
Private |
Bu kısımda tanımlanan metodlar, olaylar ve özellikler SADECE
AYNI UNIT İÇERİSİNDE tanımlanan komponentler ve sınıflar tarafından
kullanılabilir. Aynı unit içerisindeki bütün sınıf ve komponentler,
birbirlerinin private alanına erişim yetkisine sahiptir ve birbirlerine
referans olarak gösterilebilir. |
|
Protected |
Bu kısımda tanımlanan metodlar, olaylar ve özellikler, SADECE
BU SINIFTAN TÜRETİLMİŞ DİĞER SINIFLAR tarafından kullanılabilir. |
|
Public |
Bu kısımda tanımlanan metodlar, olaylar ve özellikler, her
yerden erişilip kullanılabilir. |
|
Published |
Bu kısımda Object Inspector'da görülüp değiştirilebilen
Tasarım anındaki özellikler ile olayların tanımlandığı bölümdür ve çalışılan
proje kaydedildiği zaman bu özellikler de <proje_adi>.dfm dosyasına
kaydedilirler. Bu kısımda her özellik kullanılamaz. Buna örnek olarak ARRAY
türündeki özellikler verilebilir. |
Yazacağımız yeni komponent, Win32 API aracılığı ile iki zaman
dilimi arasındaki süreyi hesaplayacak. Bunun için başlangıç ve bitiş zamanlarını
değişken olarak tanımlayacağız. Şimdi "TComponent" sınıfından yeni bir
komponent
türetip "C:\mycomp\mycomp.dpk" kütüphanesi içerisine TMyFirst adı ile
ekleyin ve dosyaya aşağıdaki kodu ekleyin.
type
TMyFirst = class(TComponent)
private
{ Private declarations }
FStart, FStop : DWord;
protected
{ Protected declarations }
function GetElapsedTime : String; virtual;
public
{ Public declarations }
procedure Start; virtual;
procedure Stop ; virtual;
property StartTime : DWord read FStart;
property StopTime : DWord read FStop;
property ElapsedTime : String read GetElapsedTime;
published
{ Published declarations }
end;
Private kısmında Başlangıç ve Bitiş zamanlarını tanımlayan
FStart ve FStop adlı iki ön değişken (Sınıf deklarasyonlarında kullanılan
değişkenlere ön değişken denmektedir. Ön değişken isimleri Standart
olarak F harfi ile başlar ve komponent özelliklerine değer atama işlemlerinde
kullanılırlar.) ile public kısmında, çalışma anında süreci başlatma ve durdurma
işlemlerini yapabilmek için Baslat ve Durdur isimli iki prosedür tanımlıyoruz.
Protected kısmında, sürecin başlaılması ile durdurulması arasındaki zamanı
hesaplamak için GetElapsedTime isimli bir fonksiyon tanımlıyoruz. Peki bu
fonksiyonu neden protected delarasyonu içerisinde tanımladık? Çünkü bu
fonksiyonun sadece, daha sonra TMyFirst sınıfından türetilecek TMySecond ve
TMyThird sınıfları tarafından kullanılmasını istiyoruz. Yine
public kısmında, çalışma anında kullanılabilecek olan StartTime, StopTime ve
ElapsedTime adlarında Salt-Okunur üç özellik tanımlıyoruz. ElapsedTime
özelliği değerini GetElapsedTime fonksiyonundan alacaktır.
Şimdi de implementation kısmına aşağıdaki kodları ekleyin.
function TMyFirst.GetElapsedTime: String;
begin
result := IntToStr(FStop - FStart);
end;
procedure TMyFirst.Start;
begin
FStart := GetTickCount; //GetTickCount Bir Windows API fonksiyonudur.
end;
procedure TMyFirst.Stop;
begin
FStop := GetTickCount; //GetTickCount Bir Windows API fonksiyonudur.
end;
(GetTickCount fonksiyonu bir Win32 API fonksiyonudur ve Windows
başlatıldığından itibaren geçen süreyi tutar.) GetElapsedTime fonksiyonu her
zaman Durdurma anındaki değer ile Başlatma anındaki değeri String olarak
döndürür ve bu değer ElapsedTime özelliğine aktarılır.
"MyFirst.pas" adlı dosyayı kaydederek "mycomp.dpk" dosyasını tekrar derleyin ve kaydedin.
İlk Komponentimizin tam kodu
Değişikliklerden sonra "MyFirst.pas" dosyasının son hali şu şekilde
olmalıdır.
unit MyFirst;
interface
uses
Windows, Messages, SysUtils, Classes;
type
TMyFirst = class(TComponent)
private
{ Private declarations }
FStart, FStop : DWord;
protected
{ Protected declarations }
function GetElapsedTime : String; virtual;
public
{ Public declarations }
procedure Start; virtual;
procedure Stop; virtual;
property StartTime: DWord read FStart;
property StopTime: DWord read FStop;
property ElapsedTime: String read GetElapsedTime;
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyFirst]);
end;
function TMyFirst.GetElapsedTime: String;
begin
result := IntToStr(FStop - FStart);
end;
procedure TMyFirst.Start;
begin
FStart := GetTickCount; //GetTickCount Bir Windows API fonksiyonudur.
end;
procedure TMyFirst.Stop;
begin
FStop := GetTickCount; //GetTickCount Bir Windows API fonksiyonudur.
end;
end.
komponentin Test Edilmesi
Yeni proje başlatın. Ana Formun üzerine komponent paletinin "Samples"
kısmından "Myfirst" isimli komponentimizden bir tane indirin. Daha sonra iki adet Button komponenti indirip butonların adlarını btnBaslat ve btnDurdur olarak, Caption
özelliklerini sırasıyla "BAŞLAT" ve "DURDUR" olarak değiştirip aşağıdaki kodları ilgili
butonların "OnClick" olaylarına ekleyin.
procedure TForm1.btnBaslatClick(Sender: TObject);
begin
MyFirst1.Start;
end;
procedure TForm1.btnDurdurClick(Sender: TObject);
begin
MyFirst1.Stop;
Caption := MyFirst1.ElapsedTime;
end;
Projeyi derleyip çalıştırın ve önce "BAŞLAT" butonuna basın. Biraz
bekledikten sonra "DURDUR" butonuna basıp Formun başlık çubuğunda iki butona
basma arasında geçen sürenin milisaniye olarak gösterildiğini gözleyin.
Virtual, Dynamic ve Override
TMyFirst isimli komponenti yazarken Start, Stop ve
GetElapsedTime tanımlarının sonlarındaki virtual kelimesi dikkatinizi
çekmiş olmalı. Bu tür tanımlayıcılar bir temel sınıftan devralınan bir metodun,
yeni sınıf içerisinde hangi yöntemle çalışacağını belirlemek içindir. Bu
tanımlayıcılar aşağıdaki gibi kısaca özetlenebilir:
Virtual ve Dynamic tanımlayıcıları oluşturulacak temel sınıftaki metodun yeni
metoda hangi şekilde devralınacağını belirlemek için kullanılırlar. Aralarında
sadece kullanılacak RAM miktarının büyüklüğü ve işlem hızı açısından bir fark
vardır. Dynamic olarak tanımlamada daha az RAM kullanımının yanısıra işlem
hızının yavaşlaması, Virtual olarak tanımlamada ise kullanılan RAM miktarının
artmasına karşın işlem hızının artması ön plana çıkar.
Override tanımlayıcısı ile temel sınıftaki metodlardan birinin yerine
çalıştırılacak yeni bir metod tanımlamak için kullanılır. Yeni metod temel
sınıftaki metod yerine çalıştırılacak olmasına rağmen temel sınıftaki metoda
bağlı olarak çalıştırılabilir. Buna Inheritance denir ve temel sınıf
içerisindeki metodu çağırdıktan sonra buna bağlı olarak yeni işlemler de
yapılabilir.
Şimdi yukarıda anlatılanları bir örnek ile açıklamaya çalışalım.
"TMyFirst" sınıfından yeni bir komponent türetin ve bu yeni
komponentin adını da "TMySecond" olarak atadıktan sonra ilk komponentte
yaptığımız işlemleri aynı şekilde yaparak "install" edin. Bu
şekilde TMyFirst sınıfının tüm özelliklerine (FStart ve FStop ön
değişkenleri, Start ve Stop prosedürleri, GetElapsedTime fonksiyonu ile
StartTime, StopTime ve ElapsedTime özelliklerinin tamamına) sahip olan TMySecond adlı
yeni bir sınıf türetmiş oluyoruz.
Şimdi de Type
bloğu içerisindeki Protected kısmını aşağıdaki gibi değiştirin:
protected
{ Protected declarations }
function GetElapsedTime : String; override;
Bu şekilde bir ekleme ile TMyFirst.GetElapsedTime adlı metodu yerine,
TMySecond sınıfında yeni bir metod tanımlayarak TMyFirst sınıfının
GetElapsedTime fonksiyonunu "override" ediyoruz.
Implementation kısmına da aşağıdaki kodu ekleyin:
function TMyFirst.GetElapsedTime: String;
var
S : String;
begin
S := inherited GetElapsedTime;
result := 'Elapsed Time calculated as ' + S + ' milisecond(' + Format('%.2f second).',
[(StopTime - StartTime) / 1000]);
end;
Böylece TMySecond.GetElapsedTime metodunun her çağırılışında
TMyFirst.GetElapsedTime metodu çalıştırılarak sonuc (S adlı) bir String
değişkene aktarılacak ve bu String değişken kullanılarak
TMySecond.GetElapsedTime fonksiyonunun ürettiği çıktı belli bir formatta
elde edilecektir.
Burada dikkat edilmesi gereken önemli durumlar var.
- Eğer "TMyFirst" isimli komponent install edilmemişse "TMySecond"
isimli komponent çalışmayacaktır. Çünkü "TMySecond" komponentinin
bir metodu "TMyFirst" komponentinden override edilerek
alınmıştır.
- Doğrudan "TMySecond" install edilirse Delphi "mycomp.dpk"
dosyasına "TMyFirst" adlı komponenti de otomatik olarak ekleyip
derlemeyi deneyecektir.
- Eğer "TMyFirst" install edilmiş ancak protected
kısmındaki GetElapsedTime fonksiyonu virtual olarak
belirtilmemişse "TMySecond" komponenti bu fonksiyonu override
edemez.
|
MySecond komponentinin tam kodu
MySecond.pas dosyasının son hali aşağıdaki gibi olmalıdır. (Interface kısmındaki uses ifadesine MyFirst adlı unit'in de eklenmiş olduğuna dikkat
edin.)
unit MySecond;
interface
uses
Windows, Messages, SysUtils, Classes, MyFirst;
type
TMyFirst = class(TComponent)
private
{ Private declarations }
FStart, FStop : DWord;
protected
{ Protected declarations }
function GetElapsedTime : String; override;
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMySecond]);
end;
function TMyFirst.GetElapsedTime: String;
var
S : String;
begin
S := inherited GetElapsedTime;
result := 'Elapsed Time calculated as ' + S + ' milisecond(' + Format('%.2f second).',
[(StopTime - StartTime) / 1000]);
end;
end.
Yeni "MySecond.pas" dosyasını kaydedip "mycomp.dpk" dosyasını
tekrar derleyin. Artık Komponent paletin "Samples" kısmında "MySecond"
adlı yeni bir komponent daha göreceksiniz.
Bu komponenti test etmek için ilk komponenti test etmek için kullandığımız
projenin aynısını "MySecond" komponentini kullanarak tekrarlayın ve
programın başlık çubuğundaki değişimi gözleyin.
Komponente Olay Ekleme
"TMySecond" adlı komponentimizden "TMyThird" adlı yeni bir komponent
türeterek "Install" edin ve oluşturulan "MyThird.pas" dosyasını kaydedin.
"TMyThird" adlı komponentimizi olaylar (events) eklemek için kullanacağız.
Olaylar Komponentin uygulamalarımızla haberleşebilmesi için gereklidir.
Komponentimizle ilgili işlemlerin yapılabilmesi ve yapılan işlemlerin uygulama
içerisinde kontrol edilebilmesi amacıyla olaylar kullanılmaktadır.
Yeni TMyThird sınıfımıza ait TYPE Bloğunu aşağıdaki gibi değiştirin ve
buradaki sınıf deklarasyonlarını ayrı ayrı açıklayalım.
TYPE
TState = (stStarted, stStopped);
TStateChangeEvent = PROCEDURE(Sender: TObject; State: TState) OF OBJECT;
TMyThird = CLASS(TMySecond)
private
{ Private declarations }
FState: TState;
FOnStart, FOnStop: TNotifyEvent;
FOnStateChange: TStateChangeEvent;
FVersion: String;
FCopyright : String;
protected
{ Protected declarations }
public
{ Public declarations }
CONSTRUCTOR Create(AOwner: TComponent); override;
DESTRUCTOR Destroy; override;
PROCEDURE Start; override;
PROCEDURE Stop; override;
FUNCTION GetVersion: String;
PROPERTY State: TState read FState;
published
{ Published declarations }
PROPERTY OnStart: TNotifyEvent read FOnStart write FOnStart;
PROPERTY OnStateChange: TStateChangeEvent read FOnStateChange write FOnStateChange;
PROPERTY OnStop: TNotifyEvent read FOnStop write FOnStop;
PROPERTY Copyright: String read FCopyright write FCopyright;
PROPERTY Version: String read GetVersion write FVersion;
END;
Tip tanımlarımız içerisine, Yalnızca stStarted ve stStopped
değerlerine sahip olabilecek "TState" sınıfını ve "TState"
sınıfından olan "State" değişkenin değerini döndürecek olan "TStateChangeEvent"
adlı bir olay (Event) prosedürü tanımlıyoruz.
PRIVATE deklarasyonu
Görüldüğü gibi "TState" sınıfına ait "FState" adlı bir ön değişken tanımladık.
Bunun anlamı "FState" ön değişkenin değeri sadece "stStarted" ve "stStopped"
değerlerinden birine sahip olabilir ve biz bu değerlere atama işlemlerini "Start"
ve "Stop" prosedürleri içerisinde yapacağız.
"FOnStart" ve "FOnStop" ön değişkenleri ise "TNotifyEvent" sınıfındandır.
Komponentimize "OnStart" ve "OnStop" olaylarını eklemek için kullanıyoruz.
"OnStart"
ve "OnStop" olayları "Published property" olarak (Object Inspector'da görülecek
şekilde) tanımlanacak ve eğer kullanıcı bu olaylar için bir kod atamış
(Assigned) ise, public olarak deklare edilmiş olan "Start" ve "Stop" prosedürleri
içerisinden çağırılarak çalıştırılacaktır.
"FOnStateChange" ön değişkeni de "TStateChangeEvent" sınıfından bir değişkendir.
Ama "TStateChangeEvent" bir prosedür olarak tanımlanmış olduğundan, "State"
değişkeninin durumunu parametre olarak kullanabilecek olan ve Bizim "Published
Property" olarak tanımladığımız "OnStateChange" olayına
"State" parametresine değer olarak atar. (Kullanıcı Object Inspector'daki OnStateChange olayına kod
yazma için çift tıkladığında aşağıdakine benzer bir prosedür tanımlaması
oluşturulur:
procedure TForm1.MyThird1ChangeState(Sender: TObject; State: TState);
begin
end;
Böylece kullanıcı isterse "State" değişkeninin değerini "OnStateChange" olayı
içerisinde kontrol ederek kullanabilir. Buna örnek olarak şöyle bir kod
yazılabilir.
procedure TForm1.MyThird1ChangeState(Sender: TObject; State: TState);
begin
btnStart.Enabled := State = stStopped; //Sonuç False olacaktır. Buton Disable Edilir.
Label1.Caption := 'Sayma işlemi başlatıldı.';
end;
"FCopyright" ve "FVersion" adlı iki String değişken
tanımlıyoruz. Bu değişkenlerin kullanımını published deklarasyonu kısmında
göreceğiz.
PUBLIC deklarasyonu
komponentimizin oluşturulabilmesi (create) için gereken constructor ve
işlemin sona ermesiyle yok edilebilmesi (Destroy) için gereken destructor
prosedürleri "TComponent" sınıfından override edilerek tanımlanıyor.
"Start" ve "Stop" prosedürleri de "TMySecond" sınıfından
override edilerek
alınıyor.
Komponentimizin sürüm bilgisini döndürecek "GetVersion" isimli bir
fonksiyon tanımlıyoruz.
Çalışma anında değeri yalnızca okunabilecek (Read-Only) şekilde "TState"
sınıfının değerlerinden (stStarted veya stStopped) sadece birine eşit olacak
"State" adlı özelliği (Property) tanımlıyoruz. (Public deklarasyonların sadece
çalışma anında kullanılabildiğini hatırlayın.) Bu değerin değiştirilebilmesi
yine çalışma anında "Start" ve "Stop" prosedürleri aracılığı ile
gerçekleştirilebilecektir. "State" özelliği tasarım anında bir değere sahip
olamayacağı için published kısmında da tanımlanamaz.
PUBLISHED deklarasyonu
Published deklarasyonları, hatırlayacağınız gibi, tasarım anında
tanımlanabilen ve değiştirilebilen komponent özellikleri için kullanılıyordu.
Komponentimiz, "OnStart", "OnStop" ve "OnStateChange" adlı
üç olaya sahip ve bunlardan "OnStart" ve "OnStop" özellikleri,
değerlerini "TNotifyEvent" sınıfının özelliklerine sahip "FOnStart"
ve "FOnStop" ön değişkenlerinden alıyor ve veriyor. Yani "TNotifyEvent"
sınıfını tetikleyerek "TMyThird" sınıfı ile ilgili bir olay meydana
geldiğini bildiriyor ve gerekli kodun çalıştırılmasını sağlıyor.
"OnStateChange" ise "TStateChangeEvent" ile "State"
özelliğini değer olarak göndererek bir prosedür tanımlıyor ve "State"
özelliğinde bir değişiklik olduğu zaman tetikleniyor.
Unutmayınız ki, olay türündeki özellikler, sadece kendileri için tanımlanmış
bir kod varsa çalıştırılırlar. Kendileri için bir kod yazılıp yazılmadığını "Assigned"
fonksiyonu ile kontrol edilmeli ve varsa çalıştırılmalıdır.
"Copyright" özelliği hem okunabilir, hem yazılabilir (kullanıcı
tarafından değiştirilebilir) bir özelliktir. Değerini "FCopyright" ön
değişkeninden alır ve yine "FCopyright" değişkenine yazar. Böylece,
başlangıç değeri constructor içinde atanmasına rağmen, kullanıcı hem tasarım hem
de çalışma anında bu özelliği istediği gibi değiştirebilir.
"Version" özelliği ise read ve write olarak tanımlanmış
string değerleri olmasına rağmen sadece okunabilir (Read-Only) durumdadır. Çünkü
kullanıcı tasarım anında veya çalışma anında değiştirmek istese bile değeri hep
aynı kalacaktır. Değer atama işlemi GetVersion fonksiyonu çağırılarak yapılır ve
elde edilen değer tekrar "FVersion" ön değişkenine aktarılır. Ve her
değer okuma işleminde "GetVersion" fonksiyonu çağırılacağı için değer hep
aynı kalacaktır.
IMPLEMENTATION
"Constructor" ile "TMySecond" Sınıfı override edilerek
oluşturulma anındaki tüm özellikleri inherited ile devralınmakta ve yeni
başlangıç değerleri tanımlanmaktadır. ("TMySecond" ve "TMyFirst"
sınıfları için "Constructor" yazmamıştık. Ancak bu iki sınıf herhangi bir
olaya sahip olmadığı için gerek yoktu ve onlar doğal olarak "TComponent"
sınıfının constructor'ünü devralıyorlardı.) Yeni değerler "FState" ön
değişkenine "stStopped" değerinin atanması (Sınıfın oluşturulması anında
bir değer verilmesi olası hataları engellemek içindir. Çünkü "FState"
değişkeni, sınıfın oluşturulması anında boş bir değere sahiptir ve bu bir
istisna (exception) oluşturur.) ve "FCopyright" ön değişkenine
kopya hakları bilgisinin (String) atanması işlemidir.
"Destructor" ile yine "TMySecond" komponenti override
edilerek inherited ile yok edilme (destroy) bilgisi alınmakta ve
değişiklik yapılmadan kullanılmaktadır. Böylece "TComponent" sınıfının
yok edilme işlemi aynen uygulanmaktadır.
"TMyThird" sınıfının "Start" ve "Stop" prosedürleri de "TMySecond"
sınıfının aynı adlı prosedürlerinden override edilerek inherited
ile çalıştırmaktadır. Ancak bu kez prosedürlerin çağırılmasından sonra "FState"
ön değişken değeri (Dolayısıyla "State" özelliğinin değeri)
değiştirilmekte ve olaylar için kod yazılıp yazılmadığına bakılmakta, kod varsa
olayları tanımlayan kodlar çağırılmaktadır.
Olaylar için kod yazılıp yazılmadığı "Assigned" fonksiyonu ile kontrol
edilmekte, eğer sonuç True (kod yazılmış) ise olay, kendisine ait
olduğunu kabul ettiği bu kodları "self" parametresi ile çağırmaktadır.
"OnStateChange" olayının çağırılması ise "self" parametresine
ek olarak "State" değerini de koda parametre olarak aktarılarak
yapılmaktadır.
"GetVersion" fonksiyonu hep aynı sonucu döndürecek şekilde
tasarlanmıştır ve döndürülen sonuç "Version" özelliğine değer olarak
verilmektedir.
"TMyThird" komponentinin Object Inspector'daki Properties paletinin
görünümü Şekil 4'te, Events paletinin görünümü Şekil-5'te verilmiştir.
Şekil 4 - TMyThird Properties |

Şekil 5 - TMyThird Events |
TMyThird komponentinin tam kodu
"TMyThird.pas" dosyasının son hali aşağıda görüldüğü gibidir.
UNIT MyThird;
INTERFACE
USES
Windows, Messages, SysUtils, Classes, MyFirst, MySecond;
TYPE
TState = (stStarted, stStopped);
TStateChangeEvent = PROCEDURE(Sender: TObject; State: TState) OF OBJECT;
TMyThird = CLASS(TMySecond)
private
{ Private declarations }
FState: TState;
FOnStart, FOnStop: TNotifyEvent;
FOnStateChange: TStateChangeEvent;
FVersion: String;
FCopyright : String;
protected
{ Protected declarations }
public
{ Public declarations }
CONSTRUCTOR Create(AOwner: TComponent); override;
DESTRUCTOR Destroy; override;
PROCEDURE Start; override;
PROCEDURE Stop; override;
FUNCTION GetVersion: String;
PROPERTY State: TState read FState;
published
{ Published declarations }
PROPERTY OnStart: TNotifyEvent read FOnStart write FOnStart;
PROPERTY OnStateChange: TStateChangeEvent read FOnStateChange write FOnStateChange;
PROPERTY OnStop: TNotifyEvent read FOnStop write FOnStop;
PROPERTY Copyright: String read FCopyright write FCopyright;
PROPERTY Version: String read GetVersion write FVersion;
END;
PROCEDURE Register;
IMPLEMENTATION
PROCEDURE Register;
BEGIN
RegisterComponents('Samples', [TMyThird]);
END;
CONSTRUCTOR TMyThird.Create(AOwner: TComponent);
BEGIN
INHERITED;
FState := stStopped;
FCopyright := '©Copyright by Mustafa GÖKMEN, Konya, 2002';
END;
DESTRUCTOR TMyThird.Destroy;
BEGIN
INHERITED;
END;
PROCEDURE TMyThird.Start;
BEGIN
INHERITED;
FState := stStarted;
IF Assigned(OnStart) THEN
OnStart(Self);
IF Assigned(OnStateChange) THEN
OnStateChange(Self, State);
END;
PROCEDURE TMyThird.Stop;
BEGIN
INHERITED;
FState := stStopped;
IF Assigned(OnStop) THEN
OnStop(Self);
IF Assigned(OnStateChange) THEN
OnStateChange(Self, State);
END;
FUNCTION TMyThird.GetVersion: String;
BEGIN
result := 'TMyThird Component v1.0';
END;
END.
Test Drive
Aşağıda Demo bir programın çalışma anında üç farklı çıktısı alınmıştır. Her
bir resimdeki Formun başlık çubuğu ile Durum çubuğu üzerindeki bilgilere dikkat
edin.

Şekil 6 - Başlangıç anı. Herhangi bir butona basılmadı. |

Şekil 7 - "Start" butonuna basıldı. |

Şekil 8 - "Stop" butonuna basıldı. |
Bu dökümanda yazımı anlatılan TMyFirst, TMySecond ve TMyThird komponentleri
ile bir Demo programı Gokmen Portal
Download bölümünden (mycomp.zip) indirebilirsiniz.
©Copyright by
Mustafa GÖKMEN,
S.Ü. Engineering Faculty, Dept. of Computer Eng, Konya/TURKIYE, 2002. All rights
reserved.
Visit Gokmen Portal (http://gokmen.selcuk.edu.tr)
to get information.