الرئيسية > مقالات, برمجة, دروس > كيف تكتب Component أول عنصر بالـ Delphi

كيف تكتب Component أول عنصر بالـ Delphi

المقالة تشرح كتابة أول عنصر مرئي في الدلفي

Unit جديدة

unit Unit1;
interface
implementation
end.


قم بتخزينها و تسميتها   MyButtons

unit MyButtons;

استخدم المكتبات التالية

uses
Windows,Classes,Controls;

الآن نشتق الكلاس الأساسي لبناء أي عنصر جديد و فارغ و نسميه TMyButton

type
TMyButton=class(TCustomControl)
end;

نقوم باشتقاق تابع الرسم لهذا الكلاس

public
procedure Paint;override;

شكل الوحدة بعد هذا كله

unit MyButtons;

interface

uses
Windows,Classes,Controls;

type
TMyButton=class(TCustomControl)
public
procedure Paint;override;
end;

implementation

end.

ضع المشيرة عند سطر تعريف التابع Paint
اضغط Ctrl+Shift+C
فيضيف المعالج جسم التابع في أسفل الوحدة

ارسم ماتشاء مثلا اسم دائرة

Canvas.Ellipse(0,0,ClientWidth,ClientHeight);

لماذا ClientWidth و ليس Width لأن Width تشمل حواف Border العنصر أي خارج مجال الرسم و التي تسمى NonClient  و ترمز بـ NC أم مجال الرسم المتاح لك هو ضمن الـ Client وهو محدد في التابع السابق الأن و بلا طول سيرة أضف قبل implementation التابع التالي

procedure Register;
[/sourcecode]

و بعد الـ implementation  بعده مباشرة أو في نهاية الوحدة جسم هذا التابع

procedure Register;
begin
RegisterComponents('MyControls',[TMyButton]);
end;

انتبه أن Register حساسة لأحرف الكبيرة و الصغيرة (حالة خاصة)

انتهى بناء العنصر علينا الآن تسجيله للمرة الأولى فقط قبل أن نتابع في برمجته

اذهب إلى قائمة الدلفي و حدد Component  ثم Install Component
لاحظ أن الدلفي سيضيف عنصرك إلى dclusr.dpk إذا أصبحت محترفا فاصنع Package خاص بك و لكن الآن
اضغط موافق  OK
سوف يسألك هل تريد بناء المكتبة أجب نعم Yes
مبارك لقد صنعت أول عنصر لك و النص الكامل هنا


unit MyButtons;

interface

uses
Windows,Classes,Controls;

type
TMyButton=class(TCustomControl)
public
procedure Paint;override;
end;

procedure Register;

implementation

{ TMyButton }

procedure TMyButton.Paint;
begin
inherited;
Canvas.Ellipse(0,0,ClientWidth,ClientHeight);
end;

procedure Register;
begin
RegisterComponents('MyControls',[TMyButton]);
end;

end.

لا تنسى أن تحفظ Save عند الخروج

سنتابع فيما بعد كيف نستقبل حدثي الـ MouseDown و الـ MouseUpعالجنا في صناعة العناصر في الدرس السابق عنصر Rasim و من أجل وضع هدف جيد و جديد
ستعيدوا الدرس السابق و لكن بتسمية العنصر Rasim و الهدف هو صنع عنصر يقوم برسم معادلة ذات متحولين
متحول X و متحول Y مثل تابع جيبي Sin

لان صنع زر أصبح قديم و الانترنيت مليئة بمثل هذه العناصر

الوحدة كما سنبدأ بها من آخر نقطة في الدرس السابق


unit Rasimes;

interface

uses
Windows,Classes,Controls;

type
TRasim=class(TCustomControl)
public
procedure Paint;override;
end;

procedure Register;

implementation

{ TRasim }

procedure TRasim.Paint;
begin
inherited;
//painting here what you want
Canvas.Rectangle(0,0,ClientWidth,ClientHeight);
end;

procedure Register;
begin
RegisterComponents('MyControls',[TRasim]);
end;

end.

لاحظ لدينا منهج Method مخصص لعملية الرسم و يسمى Paint هذا المنهج ينفذ عن حاجة الويندوز لرسم العنصر
أي أننا لا نرسم وقت مانشاء على العنصر حتى لا تتعارض البرامج مع بعضها أو إذا كان العنصر مخفيا فلا داعي لرسمه
في هذه الحالة يقوم ويندوز بإرسال رسالة للعنصر المطلوب رسمه عند الحاجة وهو الذي يدير هذه العملية و ليس أنت

لاحظ كلمة Override هذا يعني أنني أقوم باستلام العملية بدلا عن العنصر الأساسي الذي قمت بالاشتقاق منه وهو TCustomControl
يعني أن تابع الرسم هنا أنا الذي سأتولى عملية الرسم  فيه لا التابع الأصلي
ولكن ماذا لو أردت أن يقوم تابع الرسم الأصلي بالرسم أيضا ؟
مثلا تابع الرسم لـ TCustomControl يرسم دائرة (لا تصدق) و أنا سأرسم مربع،
ولكن أريد من تابع الرسم القديم أن ينفذ أولا ثم تابعي، يرسم الدائرة ثم أرسم مربعي
هنا في هذه الحالة أضع inherited و المقصود بها نفذ التابع القديم، و طبعا اين تضعها يكون تسلسل التنفيذ.
و بدون Inherited سيتم رسم مربع فقط.

ملاحظة : طبعا TCustomControl لا يرسم دائرة هذا فقط للتوضيح و لمن تخيل لو أننا اشتققنا TButton مثلا.

و الأن مارأيكم باستلام منهج جديد و ليكن Click

ما هو شكله

procedure Click; override;

أين نضعه : نستطيع وضع في الجزء public من العنصر و لكن ذلك سيتيح للمطور (المستخدم للعنصر) أن يطلب هذا التابع
مثل Rasim1.Click إذ أردت توفير ذلك للمطور فلا بأس خليه في الجزء العام (راجع درس الخائص العناصر)

مكانه الطبيعي هو protected فيصبح على الشكل التالي

TRasim=class(TCustomControl)
protected
procedure Click; override;
public
procedure Paint;override;
end;

الأن اضغط ctrl+shift+c ليقوم الدلفي بإكمال الكتابة و سوف يكتب بعد الـ implementation جسم التابع أليا (يا سلام على التنبلة)

procedure TRasim.Click;
begin
inherited;

end;

ماذا سنفعل ؟
لا تحاول الرسم هنا أذا أردت أن تكون نظاميا، إلا إذا كنت تصنع برنامج رسم (خود خبرتي)

ضع تعليمة Beep  و أضف الوحدة SysUtils إلى الـ Uses قم بـ Compile للـ Package التي تحوي الوحدة Unit التي تكتب فيها.
أذا لم تجد الـ Package فأذكرك بـ dclusr.dpk الموجود في

C:\Program Files\Borland\Delphi7\Lib

التابع أصبح كالتالي

procedure TRasim.Click;
begin
inherited;
beep;
end;

الآن جربه على مشروع جديد و لاحظ بعد تنفيذ البرنامج صوت beep عند النقر على العنصر (شغل مكبر الصوت إذا لم تسمعه)

و بهذه الطريقة تعلمنا استلام المناهج و هي طريقنا لإنشاء الأحداث Event لأن الأحداث تطلب من خلال المناهج،
يعني لما لا نصنع حدث OnClick

الحدث هو عبارة عن متحول لتابع يتم طلبه من أي مكان من برنامج العنصر شريطة أن يكون هذا النتحول معبأ حتى لا ينفجر البرنامج.
و هذا المتحول يعرّف كخاصية حتى يستطيع المطورة التعامل معه.
مثال
أضف إلى جسم العنصر السطر التالي

public
procedure Paint;override;
property OnClick : TNotifyEvent read FOnClick write FOnClick;  // this line
end;

ثم إضغط Ctrl+ALt+C

سيتم إضافة المتحول في المقطع private من جسم العنصر
هذا الحدث لن يظهر للمطور لأنه يوجد في القسم public
ضع كلمة published قبل سطر تعريف الخاصية ليصبح

published
property OnClick : TNotifyEvent read FOnClick write FOnClick;
end;

قم بـ Compile لـ Package

جرب العنصر و لاحظ و جود خاصية OnClick و لكن لا تجربها لأنها لم تعمل بعد لأننا لم نطلها في مكان من عملنا السابق

كيف نطلب الحدث : ببساطة بمجرد ذكر اسمه مع معطياته كأي تابع عادي

procedure TRasim.Click;
begin
inherited;
beep;
OnClick(Self); //this line
end;

و لكن ماذا لو كان المطور لم يستعمله أصلا، هنا علينا التأكد من أن المتحول لاتساوي قيمته nil

procedure TRasim.Click;
begin
inherited;
beep;
if OnClick <> nil then
OnClick(Self);
end;

نحن المبرمجين نفضل استعمال المتحول و ليس الخاصية، و نحب استعمال Assigned للتأكد من أن المتحول لا يساوي nil

procedure TRasim.Click;
begin
inherited;
beep;
if Assigned(FOnClick) then
FOnClick(Self);
end;

قم بـ Compile لـ Package
و جرب العنصر و لا تنسى إزالة beep من العنصر لأنها للتجريب فقط و تصبح الوحدة Unit كالتالي

unit Rasimes;

interface

uses
Windows, SysUtils, Classes,Controls;

type
TRasim=class(TCustomControl)
private
FOnClick: TNotifyEvent;
protected
procedure Click; override;
public
procedure Paint;override;
published
property OnClick : TNotifyEvent read FOnClick write FOnClick;
end;

procedure Register;

implementation

{ TRasim }

procedure TRasim.Click;
begin
inherited;
if Assigned(FOnClick) then
FOnClick(Self);
end;

procedure TRasim.Paint;
begin
inherited;
//painting here what you want
Canvas.Rectangle(0,0,ClientWidth,ClientHeight);
end;

procedure Register;
begin
RegisterComponents('MyControls',[TRasim]);
end;

end.

سنتابع في الدرس القادم استكمال راسم التوابع بإضافة حدث OnFunc .كيف نضيف خاصية جديدة للعنصر الذي نشتقه

يوجد لكل فئة Class أربع أنواع من المتحولات (الخواص) property و الأوامر (مناهج) method و كل منها يسمى عضو (members) لهذه الفئة
مثال

type
TMyButton=class(TCustomControl)
private
MyCaption:string;         // this is a member
function DoSomthing:Boolean; // this is a member
protected               // this is a member
MyText:string;  // this is a member
public
MyValue:Integer // this is a member
procedure Paint;override;   // this is a member
end;

وهي مصنفة إلى

private
protected
public
published

الـ private
لا يمكن التعامل معه إلا من خلال في نفس الوحدة Unit التي كتب فيها هذا العضو مثل MyText
و هو يستخدم إذا أردنا من مشتقي العناصر أن لا يتجاوزوا حدودهم و يتعاملون مباشرة مع هذه الأعضاء
من الصعب شرح هذا للمبتدئين فذلك يتطلب خبرة في البرمجة للمبرمجين.

الـ protected
لا يمكن التعامل معه إلا من خلال في نفس الوحدة Unit التي كتب فيها هذا العضو أو في جسم الفئات المشتقة من هذه الفئة
مثل DoubleBuffered هي خاصية عير متاحة للمبرمج العادي فقط للمبرمج الذي استق هذا العنصر (Component) أو الفئة Class

الـ public
يمكن التعامل معه من داخل و خارج جسم الفئة Class وهو مثل Edit1.Text فـ العضو Text هو Plublic و إلا لما استعطنا التعامل معه

الـ published
مثل public و لكن عند صناعة العناصر Components سوف يظهر كل خاصية Property في وقت التصميم ضمن Object Inspector

كيف ننشئ خاصية للعنصر
يمكن إنشاء الخاصية في أي جزء من الأجزاء الأربعة بكتابة سطر واحد على الشكل التالي

type
TMyButton=class(TCustomControl)
public
property Down:Boolean read FDown write FDown;   //this line
end;

ثم اضغط ctrl+shift+c فيكمل الدلفي بقية الكتابة ليصبح بالشكل

type
TMyButton=class(TCustomControl)
private
FDown:Boolean;
public
property Down:Boolean read FDown write FDown
end;

لقد قام باضافة سطر تعريف للمتحول FDown تلقائيا (انبسطوا ياكسالى)
ماذا لو كتبنا

type
TMyButton=class(TCustomControl)
public
property Down:Boolean read FDown write SetDown;  //this line
end;

ثم ctrl+shift+c سيضيف تابع لاسناد الخاصية بدلا من استعمال المتحول بذاته
ما الفائدة من من تابع الاسناد، فائدته هو أنني عندما اقوم باسناد قيمة للخاصية Down فالتابع سوف ينفذ و بامكاني إضافة تعليمة Refresh لإعادة رسم العنصر Component
مثل

Label1.Caption:='hello';

عند اسناد قيمة للخاصية Caption يتم إعادة رسم العنصر لتظهر الكتابة الجديدة عليه أليس كذلك؟
لنرى ماحل بعنصرنا

TMyButton=class(TCustomControl)
private
FDown: Boolean;
procedure SetDown(const Value: Boolean);
public
property Down:Boolean read FDown write SetDown;
end;

وفي الأسفل جسم التابع SetDown

procedure TMyButton.SetDown(const Value: Boolean);
begin
FDown := Value;
end;

نضيف إليه Refresh

procedure TMyButton.SetDown(const Value: Boolean);
begin
FDown := Value;
Refresh; //this line
end;

و الأفضل أن يكون على الشكل التالي

procedure TMyButton.SetDown(const Value: Boolean);
begin
if FDown := Value then
begin
FDown := Value;
Refresh;
end;
end;

ماذا لو كتبنا

type
TMyButton=class(TCustomControl)
public
property Down:Boolean read GetDown write SetDown;  //this line
end;

ثم ctrl+shift+c سيضيف تابع قراءة و لكن في حاتنا هذه لافائدة من تابع القراءة
تابع القراءة فيد في نريد دمج خاصيتين معا او استنتاج قيمة الخاصية عند قراءتها و ليس الاحتفاظ بالقيمة في متحول
الأفضل عدم استخدام توابع الاسناد و القراءة إلا عند الحاجة إليهما لان ذلك سيبطئ تنفيذ البرنامج (طبعا اذا كنت محترف سوف تهتم للسرعة) و لان المترجم Compiler يقوم عند عدم تحديد أي تابع باستبدال الخاصية بالمتحول نفسه مما يعني أنك فعليا تتعامل مع المتحول و ليس مع الخاصية (في الحالة الأولى بدون Set أو Get)

Advertisements
التصنيفات :مقالات, برمجة, دروس الوسوم:, , , ,
  1. لا توجد تعليقات حتى الأن.
  1. No trackbacks yet.

اترك رد

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s

%d مدونون معجبون بهذه: