View Full Version : فراخوانی رویداد مربوط به سابكامپوننت در توليد يك كامپوننت
alinikaein
شنبه 16 آبان 1388, 23:38 عصر
سلام؛
یك كامپوننت نوشتم كه دارای یك SubComponent از نوع TCustomEdit هست. در این كامپوننت، تعریف زیر را ایجاد كردم:
FEdit.OnChange := MyOnChange;
كه MyOnChange یك Procedure هست كه یك سری فعالیتهای خاصی انجام میده.
حالا از این كامپوننت استفاده میكنم و به یك Edit كه توی فرم هست، متصل میكنم. ولی مشكل اینه كه رویداد OnChange مربوط به Edit كه توی فرم نوشتم، اجرا نمیشه.
كسی میدونه راه حلش چیه؟
نمونه ديگهاي كه از اين ايراد ميتونم مثلا بزنم، اينه كه مثلاً اگه دو تا از اين كامپوننت توي فرم داشته باشم كه هر دو تا به يك Edit وصل باشه، فقط يكي از كامپوننتها ميتونه OnChange را پيگيري و اجرا كنه و كامپوننت دوم متوجه اين رويداد نميشه.
ممنون... یا علی... موفق باشید...
Mahmood_M
یک شنبه 17 آبان 1388, 23:46 عصر
به مثال زیر دقت کنید :
type
TMyComponent = Class(TComponent)
Edit : TMyEdit;
private
FMyText : String;
protected
property MyText : String read FMyText write FMyText;
constructor Create(X, Y : Integer; EParent : TWinControl);
destructor Destroy;
procedure OnChange(Sender : TObject);
end;
یک کامپوننت تعریف کردیم که یک Edit در خودش داره ، حالا به عنوان مثال می خوایم این Edit رو در زمان Create شدن کامپوننت نمایش بدیم و هر مقداری که در Edit قرار گرفت در رویداد MyText مربوط به کامپوننت قرار بگیره ، در این صورت باید رویداد برای Procedure OnChange مربوط به کامپوننت اصلی کد مورد نظر رو بنویسیم و اون رو به OnChange مربوط به Edit مربوط کنیم :
procedure TMyComponent.OnChange(Sender : TObject);
begin
MyText := Edit.Text;
end;
برای Create کامپوننت :
constructor TMyComponent.Create(X, Y : Integer; EParent : TWinControl);
begin
Edit := TEdit.Create(Owner);
Edit.OnChange := OnChange;
Edit.Left := X;
Edit.Top := Y;
Edit.Parent := EParent;
Edit.Visible := True;
end;
در کد بالا ما مختصات Left و Top و یک Parent برای Edit دریافت کرده و اون رو می سازیم و نمایش میدیم و همچنین رویداد اون رو برابر با OnChange کامپوننت اصلی قرار می دیم ...
برای Destroy هم به صورت زیر عمل می کنیم ( Edit رو Free می کنیم ) :
destructor TMyComponent.Destroy;
begin
Edit.Free;
end;
حالا اگه کامپوننت اصلی رو بسازید Edit هم نمایش داده می شه و هر مقداری که در Edit تایپ بشه در مقدار MyText کامپوننت قرار می گیره ...
اگر Edit مورد نظر شما تغییرات دیگه ای هم باید بکنه می تونید یک کامپوننت از نوع TCustomEdit ساخته و به جای TEdit یک نوع از کامپوننت خودتون رو در کامپوننت اصلی تعرفی کنید ...
البته مطمئن نیستم که منظورتون رو درست فهمیده باشم ، اگر مشکل جای دیگه است توضیح کاملتری بدید ...
موفق باشید ...
vcldeveloper
دوشنبه 18 آبان 1388, 03:46 صبح
حالا از این كامپوننت استفاده میكنم و به یك Edit كه توی فرم هست، متصل میكنم. ولی مشكل اینه كه رویداد OnChange مربوط به Edit كه توی فرم نوشتم، اجرا نمیشه.خب، یا باید متدی که شما به OnChange در داخل کد کامپوننت تان دادید به عنوان Event-handler اجرا بشه، یا متدی که کاربر به OnChange اون Edit در داخل Form Designer اختصاص میده. اگر میخواید هر دو متد اجرا بشند، یک کنترل جدید از TCustomEdit ارث بگیرید، و متدی که در TCustomEdit موجب فراخوانی رویداد OnChange میشه را پیدا کنید، و آن را در کنترل جدید خودتان override کنید. کد مورد نظرتان برای OnChange را در این متد بنویسید. دقت کنید که در کد خودتان نهایتا کد کلاس والد را با استفاده از Inherited اجرا کنید. از این کنترل جدید به عنوان SubComponent استفاده کنید، به این ترتیب، در صورت رخ دادن رویداد OnChange، ابتدا کد شما اجرا میشه، سپس کد TCustomEdit، و اگر کاربر متدی به OnChange اختصاص داده باشه، آن متد اجرا میشه.
برای Destroy هم به صورت زیر عمل می کنیم ( Edit رو Free می کنیم ) :
destructor TMyComponent.Destroy;
begin
Edit.Free;
end;
هیچ وقت Destroy را بدون Inherited ننویسید! الان این کد شما Memory Leak میده.
alinikaein
دوشنبه 18 آبان 1388, 11:33 صبح
سلام؛
اون چيزي كه من پرسيدم و دوستان جواب دادند، يه كم فرق ميكنه. يكي از دوستان خودم گفته بود يه رويداد جداگانه براي كامپوننت بسازم. اين كار را كردم. ولي به يه مشكل جديد برخورد كردم.
البته جواب آقاي كشاورز را كمي متوجه شدم و تشكر ميكنم، ولي اين كار براي هر كنترلي قابل اجرا نيست و خيلي سخته. راه ديگهاي وجود نداره كه بشه رويدادها را به صورت مضاعف هندل كرد؟
ضمناً فكر كنم درست بيان نكرده بودم دقيقاً چي ميگم:
فرض كنيد من يك Edit دارم توي فرم به نام Edit1.
يك كامپوننت ساختم مثلاً به نام MyComponent كه يكي از فيلدهاش به نام مثلاً EditBox از نوع TCustomEdit هست و مثلاً وصلش ميكنم به Edit1. حالا Edit1 يك سابكامپوننت براي MyComponent هست (اصطلاح SubComponent درسته؟)
در اين كامپوننتي كه ساختم، گفتم در رويداد OnChange مربوط به EditBox (كه الآن به Edit1 وصل شده) يك كاري انجام بده (مثلاً SubComponentOnChange (كه يك كار كاملاً مستقل هست، مثلاً يك سري عمليات پردازشي)).
حالا اگه يك كامپوننت ديگه از نوع MyComponent روي فرم بذارم و به Edit1 وصل كنم، براي اين يكي رويداد OnChange مربوط به Edit1 هندل نميشه و اون كار مستقلي كه توي كامپوننت تحت عنوان SubComponentOnChange تعريف كردم اجرا نميشه.
اين مشكل مكمل مشكلي هست كه در بالا گفتم كه هم اينجوري توضيح ميدم:
وقتي مرحله يك را انجام ميدم، يعني Edit1 را به عنوان فيلد EditBox كامپوننت خودم انتخاب ميكنم، ديگه رويداد OnChange مربوط به Edit1، هندل نميشه و هر دستوري هم كه در Edit1.OnChange نوشته باشم، اجرا نميشه و فقط دستوراتي كه در SubComponentOnChange نوشتم اجرا ميشه.
در صورتي كه توضيحاتم مشخص نبود، تا يك نمونه ارسال كنم.
باز هم ممنون... يا علي... موفق باشيد...
vcldeveloper
سه شنبه 19 آبان 1388, 00:00 صبح
يك كامپوننت ساختم مثلاً به نام MyComponent كه يكي از فيلدهاش به نام مثلاً EditBox از نوع TCustomEdit هست و مثلاً وصلش ميكنم به Edit1. حالا Edit1 يك سابكامپوننت براي MyComponent هست (اصطلاح SubComponent درسته؟)
نه، SubComponent کامپوننتی هست که کامپوننت شما Owner آن باشه، به عنوان نمونه می تونید کامپوننت LabledEdit دلفی را ببینید که یک Edit هست که در داخل خودش از یک Label استفاده میکنه.
کاری که شما دارید می کنید این هست که بخشی از کاری که کامپوننت شما باید انجام بده را به یک کامپوننت دیگه واگذار می کنید تا انجامش بده. کامپوننت دوم مستقل از کامپوننت شما ست، فقط شما کارهای مورد نظرتان را به آن محول می کنید.
شما اگر می خواید کامپوننت مربوطه کاملا در اختیار کامپوننت شما باشه، باید آن را به عنوان یکی از فیلدهای کامپوننت خودتان تعریف کنید، و خودتان آن را بسازید و آزاد کنید.
راه ديگهاي وجود نداره كه بشه رويدادها را به صورت مضاعف هندل كرد؟
در دلفی بطور عادی این قابلیت وجود نداره، ولی مثلا در #C این قابلیت وجود داره. در دلفی شما می تونید برای کامپوننت هایی که خودتان می نویسید، مکانیزمی طراحی کنید که رفتار مشابه ایی از خودشان نشان دهند، ولی تقریبا کار پیچیده ایی خواهد شد. قبلا دیدم که بعضی از برنامه نویسان دلفی همچین قابلیتی را برای خودشان پیدا سازی کردند.
alinikaein
سه شنبه 19 آبان 1388, 14:36 عصر
سلام؛
فكر كنم مسئله را به صورت زير مطرح كنم، مشخصتر باشه:
به عنوان مثال، كد زير را در نظر بگيريد كه داراي يك فيلد از نوع Edit هست كه به Edit روي فرم متصل ميشه و يك فيلد از نوع String به نام TextNeed.
قراره اين كامپوننت در صورتي كه متن Edit برابر متن فيلد TextNeed بود، يه پيام نشون بده.
در عين حال، قراره در هنگامي كه درون Edit هم متني تايپ ميشه، به عنوان مثال متن عيناً در يك Lable نمايش داده بشه.
حالا اگر دو تا از اين كامپوننت روي فرم استفاده كنيم و هر دو را هم به يك Edit كه روي فرم قرار داره، فقط كامپوننت دوم، رويداد OnChange مربوط به Edit را هندل ميكنه و ساير كامپوننتها و حتي خود رويداد OnChange مربوط به Edit، اجرا نميشه.
كد مربوط به كامپوننت:
unit MyComponentTest;
interface
uses
SysUtils, Classes, Windows, StdCtrls, ComCtrls, Forms, Messages, Graphics, Controls, Dialogs;
type
TMyComponentTest = class(TComponent)
private
{ Private declarations }
FEditBox: TEdit;
FTextNeed: String;
procedure SetEditBox(AEditBox: TEdit);
procedure SetTextNeed(ATextNeed: String);
protected
{ Protected declarations }
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure OnMyChange(Sender: TObject);
published
{ Published declarations }
property EditBox: TEdit read FEditBox write SetEditBox;
property TextNeed: String read FTextNeed write SetTextNeed;
end;
procedure Register;
implementation
uses StrUtils;
procedure Register;
begin
RegisterComponents('My Component', [TMyComponentTest]);
end;
constructor TMyComponentTest.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTextNeed := '';
end;
destructor TMyComponentTest.Destroy;
begin
inherited Destroy;
end;
procedure TMyComponentTest.Notification(AComponent: TComponent; Operation: TOperation);
begin
if Operation = opRemove then
begin
if AComponent = FEditBox then FEditBox := nil;
end;
end;
procedure TMyComponentTest.OnMyChange(Sender: TObject);
var
TempString: String;
begin
inherited;
if EditBox.Text = FTextNeed then
ShowMessage(Self.Name + ': ' + FTextNeed);
end;
procedure TMyComponentTest.SetEditBox(AEditBox: TEdit);
begin
FEditBox := AEditBox;
if FEditBox <> nil then
FEditBox.OnChange := OnMyChange;
end;
procedure TMyComponentTest.SetTextNeed(ATextNeed: String);
begin
FTextNeed := ATextNeed;
end;
end.
كد مربوط به فرم:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, MyComponentTest;
type
TForm1 = class(TForm)
MyComponentTest1: TMyComponentTest;
Edit1: TEdit;
Label1: TLabel;
MyComponentTest2: TMyComponentTest;
procedure Edit1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Edit1Change(Sender: TObject);
begin
Label1.Caption := Edit1.Text;
end;
end.
و فرم به صورت متني:
object Form1: TForm1
Left = 336
Top = 149
Width = 979
Height = 563
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 440
Top = 160
Width = 32
Height = 13
Caption = 'Label1'
end
object Edit1: TEdit
Left = 440
Top = 128
Width = 121
Height = 21
TabOrder = 0
Text = 'Edit1'
end
object MyComponentTest1: TMyComponentTest
EditBox = Edit1
TextNeed = 'Test'
Left = 376
Top = 120
end
object MyComponentTest2: TMyComponentTest
EditBox = Edit1
TextNeed = 'Ali'
Left = 376
Top = 160
end
end
كه البته اين كامپوننت فقط يك مثال هست.
حالا ميخوام، وقتي درون Edit متني وارد ميشه، هر دو تا كامپوننت بتونند متن را چك كنند، و در عين حال، متن به Label هم منتقل بشه.
باز هم از همه دوستان، عليالخصوص آقاي كشاورز تشكر ميكنم.
خيلي ممنون... يا علي... موفق باشيد...
vcldeveloper
سه شنبه 19 آبان 1388, 16:48 عصر
خب، این رفتار طبیعی هست، با توجه به کدی که برای SetEditBox نوشتید؛ وقتی فرم ساخته میشه، متد Edit1Change به رویداد OnChange مربوط به Edit1 اختصاص پیدا میکنه. وقتی MyComponent1 ساخته میشه، متد OnMyChange مربوط به MyComponent1 به رویداد OnChange مربوط به Edit1 اختصاص پیدا میکنه، و دیگه Edit1Change به این رویداد متصل نیست. وقتی MyComponent2 ساخته میشه، متد OnMyChange مربوط به آن به رویداد OnChange مربوط به Edit1 اختصاص پیدا میکنه، و متدهای Edit1Change و MyComponent1.OnMyChange دیگه به اون رویداد متصل نیستند. در نتیجه فقط متد MyComponent2.OnMyChange هست که در صورت رخ دادن رویداد Edit1.OnChange اجرا میشه.
حالا شما دقیقا هدف تان از ساخت این کامپوننت چی هست؟ به چه منظوری همچین کامپوننتی طراحی کردید، و به این نتیجه رسیدید که باید یک Edit باشه که با نمونه های مختلف این کامپوننت به این شکل تعامل داشته باشه؟ شاید با دانستن هدف و منظور شما، بشه راه حل بهتری ارائه کرد.
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.