ورود

View Full Version : سوال: نحوه ایجاد Sub Property



mbshareat
دوشنبه 15 آبان 1391, 17:58 عصر
سلام
من یه سوال دارم که مربوط به بخش کامپوننتهاست اما چون امیدی نیست که کسی تو اون بخش به این زودیها جواب بده اینجا می پرسم!(اگه به بخش کامپوننت سایر شرکتها و توسعه کامپوننت برید می بینید حتی تاپیک 28 شهریور هم هنوز تو صفحه اوّله!)
من می خوام توی کامپوننتم برای قسمتهای فرسی انگلیسی عدد و نقطه رنگهای مختلف داشته باشم.
میخوام چهار تا رنگ داشته باشم که زیر مجموعه یک پروپرتی باشند.
یه چیزی مثل فونت و Constrains میخوام.
فعلا از کد زیر استفاده کردم:

TSpecialFontColor = class(TPersistent)
private
FFa,FEn,FDot,FNum:TColor;
Procedure SetFa(C:TColor);
Procedure SetEn(C:TColor);
Procedure SetDot(C:TColor);
Procedure SetNum(C:TColor);
Protected
constructor Create();
Published
Property Fa:TColor read FFa write SetFa;
Property En:TColor read FEn write SetEn;
Property Dot:TColor read FDot write SetDot;
Property Num:TColor read FNum write SetNum;
End;

TClrCheck= class(TMyControl)
Private
FSpecialFontColor:TSpecialFontColor;
..
Procedure SetSpecialFontColor(C:TSpecialFontColor);
...
protected
constructor Create(AOwner: TComponent);Override;
destructor Destroy(); Override;
..
Published
property SpecialFontColor:TSpecialFontColor read FSpecialFontColor write SetSpecialFontColor;
..
end;
..
procedure SetDefaultSFC(SFC:TSpecialFontColor);
...
implementation
...

{ TSpecialFontColor }
constructor TSpecialFontColor.Create();
begin
inherited Create;
end;
Procedure TSpecialFontColor.SetFa(C:TColor);
Begin
FFa:=C;
// Invalidate;
End;
Procedure TSpecialFontColor.SetEn(C:TColor);
Begin
FEn:=C;
// Invalidate;
End;
Procedure TSpecialFontColor.SetDot(C:TColor);
Begin
FDot:=C;
// Invalidate;
End;
Procedure TSpecialFontColor.SetNum(C:TColor);
Begin
FNum:=C;
// Invalidate;
End;

procedure SetDefaultSFC(SFC:TSpecialFontColor);
Var
I:SmallInt;
S:String;
begin
With SFC Do
Begin
Fa:=clBlack;
En:=$FF0000;
Dot:=clRed;
Num:=$6600;
End;
end;

{ TClrCheck }

constructor TClrCheck.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetParent(TWinControl(AOwner));
SpecialFontColor:=TSpecialFontColor.Create;
SetDefaultSFC(SpecialFontColor);
...
end;

destructor TClrCheck.Destroy;
begin
SpecialFontColor.Free;
inherited;
end;



با این کد یه پروپرتی دارم که چهار تا زیر مجموعه برای تعیین رنگ داره . اما مشکل اینجاست که با تغییر دادن رنگها رنگ جدید فورا اعمال نمیشه و باید برنامه رو یه بار اجرا کنم که رنگ جدید اعمال بشه مثلا رنگ انگلیسی رو عوض می کنم می بینم همون رنگ پیشفرض میمونه تا وقتی یک بار برنامه رو اجرا کنم اونوقت اعمال میشه وقتی هم به محیط طراحی بر میگردم می بینم رنگ رو گرفته و در محیط طراحی هم اعمال می کنه!
فکر کنم علتش استفاده نکردن از Invalidate در TSpecialFontColor باشه .در TPersistant نمیشه از Invalidate استفاده کرد.
از TGraphicControl هم مشتق گرفتم اما نتیجه خوبی نگرفتم!(آخه کانواسش کجا باشه!؟)
کسی هست که در این زمینه کاری کرده باشه؟

BORHAN TEC
دوشنبه 15 آبان 1391, 19:31 عصر
سلام

نحوه ساخت expandable property یا همون Sub Property یا همون زیر پروپرتی در لینک زیر توضیح داده شده است:
http://delphi.about.com/library/bluc/text/uc083101e.htm

موفق باشید...

Felony
دوشنبه 15 آبان 1391, 19:40 عصر
من وقت نداشتم کدت رو بخونم ولی چیزی که در نگاه اول به چشم میاد این هست که برای کامپوننتت Loader ننوشتی ، کامپوننت ها بعد از خوندن مقادیر ذخیره شده ( Stored ) اولین کاری که میکنن عملیات پایه گذاری ( Initialize ) هست ، در این عملیات Property با نام Loaded که به صورت Virtual تعریف شده قبل از به نمایش در آمدن فرم والد کامپوننت صدا زده میشه و کامپوننت رو مقداردهی اولیه میکنه ، کاری که شما میکنید این هست که این Property رو Override میکنید و به کامپوننتتون مقادیر Set شده رو اختصاص میدید تا زمان نمایش فرم بار بشن .

من نمونه کوچیک زیر رو الان براتون نوشتم :

unit Shape1;

interface

uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics,
Dialogs;

type
TShapeExtender = class(TPersistent)
private
FMyShapeColor: TColor;
procedure SetMyShapeColor(const Value: TColor);
public
constructor Create;
published
property MyShapeColor: TColor read FMyShapeColor write SetMyShapeColor
default clRed;
end;

type
TMyShape = class(TShape)
private
FMyExtender: TShapeExtender;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; Override;
published
property MyProperty: TShapeExtender read FMyExtender;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Samples', [TMyShape]);
end;

{ TMyShape }

constructor TMyShape.Create(AOwner: TComponent);
begin
inherited;
FMyExtender := TShapeExtender.Create;
end;

destructor TMyShape.Destroy;
begin
FMyExtender.Free;
inherited;
end;

procedure TMyShape.Loaded;
begin
inherited;
Self.Brush.Color := FMyExtender.MyShapeColor;
end;

{ TShapeColor }

constructor TShapeExtender.Create;
begin
inherited;
Self.FMyShapeColor := clRed;
end;

procedure TShapeExtender.SetMyShapeColor(const Value: TColor);
begin
FMyShapeColor := Value;
end;

end.

- کامپوننت نویسی ریزه کاری های خیلی زیادی داره ، قبل از اینکه خودت رو باهاشون درگیر کنی چند تا مقاله و کتاب در موردش مطالعه کن { کتاب Delphi Component Writer's Guide }

- فایل ضمیمه هم یک مقاله فارسی هست ، البته من نخوندمش و نمیدونم در چه سطحی هست .

- در عجبم چرا اون لینک Delphi.About که شاهین قرار داده این موضوع رو توضیح نداده ! البته وقت نکردم دقیق ببینم هدف مقاله چی بوده ، شاید این موضوع شاملش نمیشده .

@ mbshareat ؛ سوالاتت رو در بخش مناسب بپرس ، افراد زیادی تو این انجمن نیستن که به این سوالات جواب میدن ، اون ها هم تا جایی که من اطلاع دارم به همه بخش ها سرکشی میکنن .

موفق باشید .

mbshareat
دوشنبه 15 آبان 1391, 21:44 عصر
سلام
آقای عشایری من همین امروز اون آدرس رفته بودم اما چون انگلیسیم ضعیفه و کدها هم برای من مفصل محسوب میشه چیز زیادی نفهمیدم.
آقا ماهان می بخشید من کامپوننت رو نصب کردم اما وقتی روی فرم میذارم و رنگ MyProperty\MyShapeColor رو تغییر میدم نه قبل از اجرا و نه بعد از اجرای برنامکه تغییری رخ نمیده!
در هر صورت از هر دو بزرگوار ممنونم.هنوز فکر می کنم تو بخش کامپوننت به این جور سوالها جواب نمیدند(قبلا امتحان کردم) و این بخش فقط برای توضیح در مورد کاموننتهای آماده هستش.

Felony
دوشنبه 15 آبان 1391, 22:08 عصر
این کد کامل ، کد قبلی تو history پروژه بود که اشتباهی کپی کردم :

unit Shape1;

interface

uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics,
Dialogs;

type
TShapeExtender = class(TPersistent)
private
FMyShapeColor: TColor;
procedure SetMyShapeColor(const Value: TColor);
public
constructor Create;
published
property MyShapeColor: TColor read FMyShapeColor write SetMyShapeColor
default clRed;
end;

type
TMyShape = class(TShape)
private
FMyExtender: TShapeExtender;
procedure SetMyExtender(const Value: TShapeExtender);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; Override;
published
property MyProperty: TShapeExtender read FMyExtender write SetMyExtender;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Samples', [TMyShape]);
end;

{ TMyShape }

constructor TMyShape.Create(AOwner: TComponent);
begin
inherited;
FMyExtender := TShapeExtender.Create;
end;

destructor TMyShape.Destroy;
begin
FMyExtender.Free;
inherited;
end;

procedure TMyShape.Loaded;
begin
inherited;
Self.Brush.Color := FMyExtender.MyShapeColor;
end;

procedure TMyShape.SetMyExtender(const Value: TShapeExtender);
begin
FMyExtender.Assign(Value);
end;

{ TShapeColor }

constructor TShapeExtender.Create;
begin
inherited;
Self.FMyShapeColor := clRed;
end;

procedure TShapeExtender.SetMyShapeColor(const Value: TColor);
begin
FMyShapeColor := Value;
end;

end.

mbshareat
سه شنبه 16 آبان 1391, 04:14 صبح
این کد از قبلی بهتره اما هنوز اشکال کد من رو داره یعنی در زمان طراحی تنظیم پروپرتی منعکس نمیشه.بعد از اجرای برنامه اعمال میشه اما وقت برگشت باز ظاهر کامپوننت به حالت اول برمیگرده و به طور کلی فقط وقت اجرا دلفی پروپرتی رنگ مربوطه رو توی ظاهر کامپوننت اعمال می کنه.
اگر ممکنه در مورد Loaded کمی توضیح بدید. آیا تنها تاثیرش در مقدار دهی اولیه هست؟ کد من گرچه ناشیانه بود ولی در این حد کارم رو راه مینداخت.
ممنون...

Felony
سه شنبه 16 آبان 1391, 05:43 صبح
من نگفتم که در زمان طراحی هم این کار رو میکنه ، بهتون گفتم که طراحی کامپوننت ریزه کاری های زیادی داره ( مخصوصا کامپوننت های ویژوال ) ، من وقت نداشتم همه چیز رو براتون بنویسم اون تابع Loaded هم در نگاه اول به چشمم خورد ، برای کاری که میخوای انجام بدی Paint مربوط به کامپوننت رو Override کن ، یادت باشه حتما ComponentState رو چک کنی که csDesigning باشه و گرنه سربار زیادی در رویداد OnPaint به Main Thread برنامت متحمل میشه ، برای نمایش تغییرات در هنگام Drag کردن کامپوونت جدید روی فرم تو همون Constructor کامپوننت بررسی کن اگر تو حالت csDesigning بودی رنگ رو بار کن ، اگر csDesigning رو بررسی نکنی هم مشکلی پیش نمیاد ولی در هنگام اجرا 2 بار این عملیات انجام میشه یکی تو همین رویداد و یکی تو رویداد Loaded :

unit Shape1;

interface

uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics,
Dialogs;

type
TShapeExtender = class(TPersistent)
private
FMyShapeColor: TColor;
procedure SetMyShapeColor(const Value: TColor);
public
constructor Create;
published
property MyShapeColor: TColor read FMyShapeColor write SetMyShapeColor
default clRed;
end;

type
TMyShape = class(TShape)
private
FMyExtender: TShapeExtender;
procedure SetMyExtender(const Value: TShapeExtender);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; Override;
procedure Paint; Override;
published
property MyProperty: TShapeExtender read FMyExtender write SetMyExtender;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Samples', [TMyShape]);
end;

{ TMyShape }

constructor TMyShape.Create(AOwner: TComponent);
begin
inherited;
FMyExtender := TShapeExtender.Create;
end;

destructor TMyShape.Destroy;
begin
FMyExtender.Free;
inherited;
end;

procedure TMyShape.Loaded;
begin
inherited;
Self.Brush.Color := FMyExtender.MyShapeColor;
end;

procedure TMyShape.SetMyExtender(const Value: TShapeExtender);
begin
FMyExtender.Assign(Value);
end;

{ TShapeColor }

constructor TShapeExtender.Create;
begin
inherited;
Self.FMyShapeColor := clRed;
end;

procedure TMyShape.Paint;
begin
inherited;
if (csDesigning in ComponentState) then
Brush.Color := FMyExtender.MyShapeColor;
end;

procedure TShapeExtender.SetMyShapeColor(const Value: TColor);
begin
FMyShapeColor := Value;
end;

end.


اگر ممکنه در مورد Loaded کمی توضیح بدید. آیا تنها تاثیرش در مقدار دهی اولیه هست؟ کد من گرچه ناشیانه بود ولی در این حد کارم رو راه مینداخت.
بله ، Loaded قبل از نمایش فرم مالک کامپوننت صدا زده میشه ، حالا شما داخلش هر کاری میتونی بکنی ، نگران پرش کامپوننت هم نباش چون تا وقتی کار متدهای Loaded مربوط به کامپوننت ها تمام نشه فرم نمایش داده نمیشه که پرشی داشته باشه .

mbshareat
سه شنبه 16 آبان 1391, 08:06 صبح
اگه درست متوجه شده باشم تا اینجا اصول کامپوننت نویسی رو یادم دادید.
یه سوال دارم:
Brush.Color := FMyExtender.MyShapeColor;
سه جا تکرار شده. علتش چیه؟ اگه برای تاثیر در زمان طراحی باشه که هنوز هم رنگ اعمال نمیشه؟
آیا اگر همیشه مقداردهی های اولیه کامپوننتهامون (کلا همه سطرهای کد درون Create بجز ;inherited) رو داخل Loded بذاریم درست و کافی نیست؟
یه سوال دیگه.منظورتون از این جمله:

و گرنه سربار زیادی در رویداد OnPaint به Main Thread برنامت متحمل میشه
اینه که هر بار که Paint اجرا میشه سربار زیادی داره یا فقط دفعه اول که کامپوننت ترسیم میشه؟

Felony
سه شنبه 16 آبان 1391, 08:42 صبح
سه جا تکرار شده. علتش چیه؟
من همون سورس قبلی رو بنا به نیازتون Extend کردم ، وقتی متد Paint رو Override میکنید نه نیازی به اون دستور در Constructor هست و نه در Loaded ، همون Paint کافیه .


اگه برای تاثیر در زمان طراحی باشه که هنوز هم رنگ اعمال نمیشه؟
یعنی چی اعمال نمیشه من چند بار بررسی کردم و مشکلی نداشت .


آیا اگر همیشه مقداردهی های اولیه کامپوننتهامون (کلا همه سطرهای کد درون Create بجز ;inherited) رو داخل Loded بذاریم درست و کافی نیست؟
چرا ، گفتم که من کدهای قبلی رو تغییری ندادم وگرنه بهشون نیازی نیست .


اینه که هر بار که Paint اجرا میشه سربار زیادی داره یا فقط دفعه اول که کامپوننت ترسیم میشه؟
متد Paint در هر شرایطی ممکنه چند صد بار فراخوانی بشه ( فرمی بیاد روی فرم برنامه ما ، فرمی بیاد روی شئ ، موس بیاد روی شئ ، فرم برنامه Minimize یا ... بشه ) ، اگر اون شرط رو نزارید علاوه بر اینکه Inherited عملیات بار کردن مقادیر رو توسط پیاده سازی که در کلاس پایه داره انجام میده کد شما رو هم بی دلیل در هر بار فراخوانی اجرا میکنه در صورتی که تو همون inherited عملیات رسم Runtime صورت گرفته و شما فقط نیاز به مقدار دهی عملیات رسم در Designtime دارید .

در ضمن این یکی از صدها روش پیاده سازی کامپوننت ها هست ، بسته به شرایط و نهوه پیاده سازی میشه از روش های مختلفی مثل Override کردن متدهای پایه مثل Notification یا ارسال پیغام ها و ... برای به روز رسانی کامپوننت استفاده کرد ، باز هم میگم برای نوشتن کامپوننت های خوب و درست حسابی بی گدار به آب نزدنید ، کامپوننت نویسی مبحث پیچیده و پر ریزه کاری هست ، اون کتابی که در پست اولم بهتون معرفی کردم میتونه با تمام راهکارها آشناتون کنه .

mbshareat
سه شنبه 16 آبان 1391, 17:31 عصر
یعنی چی اعمال نمیشه من چند بار بررسی کردم و مشکلی نداشت .
احتمالا علتش اینه که دلفی من نسخه قدیمیه. تا یک بار برنامه رو اجرا نکنم رنگ تعیین شده در محیط طراحی اعمال نمیشه!