PDA

View Full Version : Understanding the Sender parameter in Delphi Event Handlers



m-khorsandi
یک شنبه 29 بهمن 1385, 07:49 صبح
درک پارامتر Sender درEvent Handlerهای دلفی

Event Handler و Sender

Event Handler، بخشی از برنامه ست که وظیفه ی پاسخگویی به رویدادی که اتفاق افتاده را دارد مسلماً بارها و بارها با Event Handlerها سر و کار داشته‌اید.


به Event Handler زیر که برای رویداد OnClick یک Button(که نام آن Button1 هست) نوشته شده دقت کنید :


procedure TForm1.Button1Click(Sender: TObject) ;
begin
...
end;


متد Button1Click اشاره‌گری به TObject با نام Sender می‌گیرد. هر Event Handlerیی، در دلفی، حداقل یک پارامتر Sender خواهد داشت. موقعیکه روی Button کلیک کنید، Event Handlerیی با نام Button1Click برای رویداد OnClick فراخوانی می‌شود.

پارامتر Sender، به کنترلی ارجاع دارد که متد را فراخوانی کرده است. اگر روی کنترل Button1 کلیک کنید، باعث فراخوانی Button1Click می‌شود. در واقع پارامتر Sender، یک پوینتر به شیئ Button1 هست که توسط متد Button1Click ارسال می‌شود.


اجازه دهید در بعضی از کدها شریک شویم
هنگامیکه از پارامتر Sender، آنچنانکه شاید و باید استفاده شود، می‌توان کد را به میزان باور نکردنی انعطاف پذیر کرد. پارامتر Sender این امکان را به ما می‌دهد که تشخیص دهیم چه کامپوننتی باعث شده تا این رویداد اتفاق بیفتد، استفاده از این قابلیت بسیار ساده ست و به این وسیله می‌توان Event Handlerهای یکسان را به دو (و بیشتر) سری کامپوننت متفاوت تخصیص داد.

برای مثال، فرض کنید که Button و Menu(گزینه‌ای از یک منو) یی داریم و می‌خواهیم هر دو کار یکسانی انجام دهند. کار احمقانه‌ای هست اگر برای هر کدام، Event Handler مجزا بنویسیم، یعنی دو کامپوننت، دو Event Handler و یک کار!

در دلفی برای اینکه در یک Event Handler مشترک شویم، باید مراحل زیر را انجام دهیم :
1- برای یکی از کامپوننت‌ها(مثلاً برای Button) Event Handler را بنویسید.
2- کامپوننت یا کامپوننت‌های دیگر را انتخاب کنید( مثلاً MenuItem1).
3- در Object Inspector به صفحه Event بروید.
4- Event مورد نظر را انتخاب کنید(مثلاً OnClick).
5- روی "فلش به سمت پائین" کلیک کنید تا لیست تمام Event Handlerهای موجود و سازگار با این Event را ببینید.
6- از این لیست، Event مورد نظر را انتخاب کنید (مثلاً Button1Click).

http://i15.tinypic.com/432ysco.png


به همین سادگی یک Event Handler را به رویداد OnClick دو کامپوننت Button و MenuItem تخصیص دادیم. حالا باید تشخیص دهیم که چه کامپوننتی باعث فرخوانی این رویداد شده، به این دلیل که بخشی از کد می‌تواند بین هر دو کامپوننت مشترک باشد و بخشی از کد نیز می‌تواند مخصوص کامپوننت خاصی باشد، مثال زیر را ببینید :



procedure TForm1.Button1Click(Sender: TObject) ;
begin
{ کدی که می‌تواند بین این دو کامپوننت مشترک باشد را اینجا بنویسید{
...
{ و کدهای مخصوص را در اینجا بنویسید{

if Sender = Button1 then
ShowMessage('Button1 clicked!')
else if Sender = MenuItem1 then
ShowMessage('MenuItem1 clicked!')
else
ShowMessage('??? clicked!') ;
end;


کلاً، پارامتر Sender را با نام کامپوننت مقایسه می‌کنیم.
نکته : در این مثال بعد از انجام کد مشترک بین Button و MenuItem، کد مخصوصی برای هر یک از کامپوننت‌ها نیز نوشته شده ست. حالتی نیز برای مواقعی که فراخوانی کننده این رویداد، نه Button و نه MenuItem هست در نظر گرفته شده که می‌توانید آن را در دومین else موجود در if-then-else ببینید. اما به چه دلیل ما else را نوشتیم و چه چیز دیگری می‌تواند این رویداد را فراخوانی کند؟
Buttonیی روی فرم بگذارید(احتمالاً نام آن Button2 خواهد بود) و در رویداد OnClick آن کد زیر را بنویسید :



procedure TForm1.Button2Click(Sender: TObject) ;
begin
Button1Click(Button2) ;
end;

به این شکل تاثیر else دوم را خواهید دید.

IS و AS
از آنجائیکه پارامتر Sender از نوع TObject هست، هر شیئی می‌تواند به آن تخصیص داده شود. مقدار Sender همیشه یا Control یا Component هست تا بتواند نسبت به Eventها عکس‌العمل نشان دهد. برای اینکه پیدا کنیم چه کامپوننت یا کنترلی باعث فراخوانی Event Handler شده، پارامتر Sender را بوسیله کلمات کلیدی تست می‌کنیم، برای مثال :



if Sender is TButton then
DoSomething
else
DoSomethingElse;


عملگرis را می‌بایست به جای ClassType به کار ببرید. پارامترهای عملگر is، یک شیئ و نوع کلاس هست‌، شکل کلی:
Object is classtype

مثال :


If Sender is TLabel then


حاصل این عبارت در صورتی True خواهد بود که شیئ Sender به کلاس Tlabel ارجاع داشته باشد. بنابراین می‌توان به جای Sender.ClassType = TLabel از is استفاده کرد.

عملگر as جهت تبدیل نوع استفاده می‌شود. فرضاً اگر بخواهید پارامتر Sender را که از نوع TObject هست را به کلاس TLabel تبدیل کنید تا از ویژگی‌های خاص آن استفاده کنید، می‌توانید از این عملگر استفاده کنید:



procedure TForm1.Button1Click(Sender: Tobject);
begin
(1) if Sender is TLabel then
begin
(2)) Sender as TLabel).Caption := 'for test';
) Sender as TLabel).Font.Color := clGreen;
end;
end;


(1) اگر شیئی از کلاس TLabel این رویداد را فراخوانی کرده باشد، شرط برقرار ست، (2) وقتی حاصل شرط True ست، پس می‌توان پارامتر Sender را به کلاس TLabel تبدیل کرد و Caption و Color آن را تغییر داد. مثال بالا را ‌می‌توان به صورت زیر نیز نوشت ‌:



procedure TForm1.Button1Click(Sender: Tobject);
begin
if Sender.ClassType = TLabel then
begin
TLabel(Sender).Caption := 'for test';
TLabel(Sender).Font.Color := clGreen;
end;
end;


برای درک بهتر این موضوع، مثال قبل که در مورد Event Handlerها بود را ادامه می‌دهیم، یک EditBox به فرم اضافه کنید و کد زیر را در رویداد OnExit آن بنویسید :



procedure TForm1.Edit1Exit(Sender: TObject) ;
begin
Button1Click(Edit1) ;
end;


در بخشی از همان مثال، دستور ShowMessage('??? clicked!) را نوشته‌ایم، کد زیر را جایگزین آن کنید :



{... else}
begin
if Sender is TButton then
ShowMessage('Some other button triggered this event!')
else if Sender is TEdit then
with Sender as TEdit do
begin
Text := 'Edit1Exit has happened';
Width := Width * 2;
Height := Height * 2;
end; {begin with}
end;


خوب،
اگر بر روی Button1 کلیک کنید، پیغام "Button1 clicked" ظاهر می‌شود، اگر روی گزینه ی MenuItem1 کلیک کنید، پیغام "MenuItem1 clicked" نمایش داده می‌شود. اما اگر روی Button2 کلیک کنید، پیغام "Some other button triggered this event!" ظاهر می‌شود. اما زمانیکه از کامپوننت Edit1 خارج می‌شوید چه اتفاقی می‌افتد ؟