در اين جلسه قصد دارم يک کامپوننت ساده بسازم که البته مي تونه کاربردي هم باشه.
سعي مي کنم تا حد ممکن نکاتي رو که لازمه درش بگنجونم.
کامپوننتي که در نظر دارم يک کامپوننت براي ورود زمان يا همون تايم اديت هست.
اول بياييد ببينيم اصلا چي مي خواهيم:
- يک کامپوننت با سه اديت براي ورود ساعت - دقيقه - ثانيه به ترتيب از چپ به راست
- دو عدد کولون (:) براي جدا کردن اين اديت ها از هم
- پرش اتوماتيک بين اديت ها بعد از پر شدن هر کدوم از اونها
- قابليت ست شدن زمان از روي زمان سيستم
- قابليت گرفتن خروجي به فرمت هاي عددي و رشته اي با کولون و بدون اون
- داشتن رويداد OnChange
براي ساخت اين کامپوننت، به دليل اين که چند تا آبجکت دروني داريم، نياز به يک آبجکت نگهدارنده داريم که همه اين ها رو روش بگذاريم، اين شي مي تونه يک پنل باشه يا يک گروپ باکس
من TCustomControl رو انتخاب مي کنم به اين دليل که هر چه درخت کلاس ما کم عمق تر باشه کار باهاش راحت تر و لودينگ سريع تر و آيتم هاي اضافي کمتري داره.
اين کلاس از TWinControl مشتق مي شه اما دست شما رو در کشيدن چيزي روش آزاد مي گذاره چون Canvas رو پياده سازي کرده.
انتخاب اين که از چه چيزي مشتق کنيم به ما خيلي در روند برنامه نويسي کمک مي کنه، مثلا من مي تونستم مستقيم از TComponent بگيرم که در اين صورت بايد تک تک آيتم هايي رو که در کلاس هاي TControl و TWinControl وجود داره دوباره نويسي مي کردم يا بي خيالشون مي شدم.
اسم کلاسم رو مي گذارم TU30TimeEdit،
تا اينجاي کار:
TU30TimeEdit = class(TCustomControl)
end;
خب، بريم سراغ اشيايي که بايد در اين کامپوننت وجود داشته باشه،
سه تا اديت و دو تا ليبل
TU30TimeEdit = class(TCustomControl)
protected
label1: TLabel;
label2: TLabel;
MaskEdit1: TMaskEdit;
MaskEdit2: TMaskEdit;
MaskEdit3: TMaskEdit;
end;
علت Protected بودن اين اشيا اينه که در زيرکلاسها دستمون براي دادن تغييرات در اينها باز باشه. ولي لزومي نداره که شي اونها رو عمومي اعلام کنه.
براي ساخت اونها بايد از متد سازنده کمک بگيريم:
public
constructor Create(AOwner: TComponent); override;
اجازه بديد باقي ماجرا رو روي سورس ادامه بديم.
کلاس نهايي:
TU30TimeEdit = class(TCustomControl)
private
FChangeTag: integer;
F_Author: string;
FOnChange: TNotifyEvent;
FPairKeyDownWithUp: integer;
FLastDownKey: word;
procedure Set_Author(const Value: string);
procedure AdjustWidthHeight;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure MyOnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MyOnKeyPress(Sender: TObject; var Key: Char);
procedure MyOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MyOnEnter(Sender: TObject);
procedure MyOnExit(Sender: TObject);
procedure MyOnChange(Sender: TObject);
procedure MyOnClick(Sender: TObject);
procedure MyOnDblClick(Sender: TObject);
procedure MyOnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MyOnMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure MyOnMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure SetValueInColonStringFormat(const Value: string);
procedure SetValueInIntegerFormat(const Value: integer);
procedure SetValueInStringFormat(const Value: string);
function GetValueInColonStringFormat: string;
function GetValueInIntegerFormat: integer;
function GetValueInStringFormat: string;
protected
label1: TLabel;
label2: TLabel;
MaskEdit1: TMaskEdit;
MaskEdit2: TMaskEdit;
MaskEdit3: TMaskEdit;
public
constructor Create(AOwner: TComponent); override;
property _Author: string read F_Author write Set_Author;
property ValueInIntegerFormat: integer read GetValueInIntegerFormat write SetValueInIntegerFormat;
property ValueInStringFormat: string read GetValueInStringFormat write SetValueInStringFormat;
procedure SetToCurrentTime;
published
property ValueInColonStringFormat: string read GetValueInColonStringFormat write SetValueInColonStringFormat;
property Anchors;
property BevelKind;
property BiDiMode;
property Color;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property ParentBiDiMode;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Visible;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
end;
دليل وجود CMFontChanged اينه که کامپوننت با تغيير فونت خودش رو منطبق کنه. اين متد يک متد مسيج هست که کاربردش رو مي بينيد.
در قسمت published هم يک سري افزايش ميدان ديد انجام شده تا برنامه نويس بتونه از اين آيتم ها در هنگام طراحي استفاده کنه.
قسمت پياده سازي:
{اين پروسيجر ربطي به کلاس نداره و با گرفتن يک متن اندازه هاي اون رو بر مي گردونه}
function Get_TextWidthHeightByPixel(aFont: TFont; aText: string): TWidthHeight;
var
fCanvas: TCanvas;
begin
fCanvas := TCanvas.Create;
try
fCanvas.Handle := GetDC(0);
fCanvas.Font := aFont;
Result.Width := fCanvas.TextWidth(aText);
Result.Height := fCanvas.TextHeight(aText);
finally
fCanvas.Destroy;
end;
end;
Const
TIME_24H_SIGN = 'D';
TIME_12H_SIGN = 'A';
TIME_AM_SIGN = 'am';
TIME_PM_SIGN = 'pm';
{ TU30TimeEdit }
{اين متد اندازه ها رو ريست مي کنه}
procedure TU30TimeEdit.AdjustWidthHeight;
var
WH: TWidthHeight;
FFont: TFont;
begin
FFont := Self.Font;
{اين کار اجازه مي ده اندازه ها قابل تغيير باشند}
Self.Constraints.MinHeight := 0;
Self.Constraints.MaxHeight := 0;
Self.Constraints.MinWidth := 0;
Self.Constraints.MaxWidth := 0;
{اينجا داريم ارتفاع رو بر حسب فونت جديد بدست مياريم تا ارتفاع کامپوننت رو ست کنيم}
WH := Get_TextWidthHeightByPixel(FFont, TIME_24H_SIGN);
Self.Height := WH.Height +7;
Self.label1.Height := WH.Height;
Self.label2.Height := WH.Height;
{همين کار براي درازاي ليبل هاي کولون}
WH := Get_TextWidthHeightByPixel(FFont, ':');
Self.label2.Width := WH.Width;
Self.label1.Width := WH.Width;
{همين کار براي اندازه ي آيتم ها}
WH := Get_TextWidthHeightByPixel(FFont, '99');
Self.MaskEdit3.Height := WH.Height;
Self.MaskEdit2.Height := WH.Height;
Self.MaskEdit1.Height := WH.Height;
Self.MaskEdit3.Width := WH.Width +1;
Self.MaskEdit2.Width := WH.Width +1;
Self.MaskEdit1.Width := WH.Width +1;
WH := Get_TextWidthHeightByPixel(FFont, TIME_AM_SIGN);
{کامپوننت ها بايد در جاهاي جديد دوباره بازنشاني بشوند}
Self.MaskEdit3.Left := 1;
Self.label2.Left := Self.MaskEdit3.Left +Self.MaskEdit3.Width +1;
Self.MaskEdit2.Left := Self.label2.Left +Self.label2.Width +1;
Self.label1.Left := Self.MaskEdit2.Left +Self.MaskEdit2.Width +1;
Self.MaskEdit1.Left := Self.label1.Left +Self.label1.Width +1;
Self.Width := MaskEdit1.Left +MaskEdit1.Width +6;
{اين کار باعث مي شه برنامه نويس نتونه اندازه کامپوننت رو عوض کنه و اون رو قفل مي کنه}
Self.Constraints.MinHeight := Self.Height;
Self.Constraints.MaxHeight := Self.Height;
Self.Constraints.MinWidth := Self.Width;
Self.Constraints.MaxWidth := Self.Width;
{اين متد کامپوننت رو در "اولين فرصت" رفرش مي کنه}
Self.Invalidate;
end;
procedure TU30TimeEdit.CMFontChanged(var Message: TMessage);
begin
{بايد ببينيم کامپوننت آيتم هاش وجود داره يا نه، ممکنه به هر دليلي در هنگامي که کامپوننت آماده نيست، اين متد کال بشه}
if Assigned(MaskEdit1)
and Assigned(MaskEdit2)
and Assigned(MaskEdit3)
and Assigned(label1)
and Assigned(label2) then
AdjustWidthHeight;
{اين کلمه پاييني مي گه قبلا هر کاري مي کردي حالا هم همون کار رو کن و با تغييرات من کارهاي قبلي رو در قبال اين مسيج رها نکن}
inherited;
end;
constructor TU30TimeEdit.Create(AOwner: TComponent);
begin
{اول از همه ساخت خود نگهدارنده، يعني خود کامپوننت}
inherited Create(AOwner);
{ست کردن Parent در اينجا به دليل ضعف بعضي نسخ دلفي هست که در هنگام گذاشتن کامپوننت روي فرم ايجاد مي شد}
Self.Parent := TWinControl(AOwner);
{شکل عمومي کامپوننت}
Self.BevelKind := bkFlat;
Self.Color := clWindow;
Self.Caption := '';
Self.Color := clWhite;
{ليبل ها}
Self.label1 := TLabel.Create(Self);
Self.label1.Parent := Self;
Self.label1.AutoSize := false;
Self.label1.Caption := ':';
Self.label1.Top := 4;
{خود ليبل ها بايد به رويداد ها حساس باشند، کليک روي ليبل با کيلک روي کامپوننت بايد يک معني رو بده، به همين خاطر رويدادهاي لازم رو ست مي کنيم به هندلر هاي خودمون}
Self.label1.OnClick := MyOnClick;
Self.label1.OnDblClick := MyOnDblClick;
Self.label1.OnMouseDown := MyOnMouseDown;
Self.label1.OnMouseMove := MyOnMouseMove;
Self.label1.OnMouseUp := MyOnMouseUp;
Self.label2 := TLabel.Create(Self);
Self.label2.Parent := Self;
Self.label2.AutoSize := false;
Self.label2.Caption := ':';
Self.label2.Top := 4;
Self.label2.OnClick := MyOnClick;
Self.label2.OnDblClick := MyOnDblClick;
Self.label2.OnMouseDown := MyOnMouseDown;
Self.label2.OnMouseMove := MyOnMouseMove;
Self.label2.OnMouseUp := MyOnMouseUp;
{ساخت اديت ها}
Self.MaskEdit3 := TMaskEdit.Create(Self);
Self.MaskEdit3.Parent := Self;
Self.MaskEdit3.BorderStyle := bsNone;
Self.MaskEdit3.EditMask := '99;1;_';
Self.MaskEdit3.MaxLength := 2;
Self.MaskEdit3.TabOrder := 0;
Self.MaskEdit3.Tag := 1;
Self.MaskEdit3.Text := '12';
Self.MaskEdit3.Top := 2;
{اين اديت ها هم بايد رويداد ها رو ساپورت کنند، علاوه بر اون، بايد در هنگام تغيير يا تايپ چيزي، بتونيم براشون رويداد درست کنيم}
Self.MaskEdit3.OnExit := MyOnExit;
Self.MaskEdit3.OnEnter := MyOnEnter;
Self.MaskEdit3.OnChange := MyOnChange;
Self.MaskEdit3.OnKeyDown := MyOnKeyDown;
Self.MaskEdit3.OnKeyPress := MyOnKeyPress;
Self.MaskEdit3.OnKeyUp := MyOnKeyUp;
Self.MaskEdit3.OnClick := MyOnClick;
Self.MaskEdit3.OnDblClick := MyOnDblClick;
Self.MaskEdit3.OnMouseDown := MyOnMouseDown;
Self.MaskEdit3.OnMouseMove := MyOnMouseMove;
Self.MaskEdit3.OnMouseUp := MyOnMouseUp;
Self.MaskEdit2 := TMaskEdit.Create(Self);
Self.MaskEdit2.Parent := Self;
Self.MaskEdit2.BorderStyle := bsNone;
Self.MaskEdit2.EditMask := '99;1;_';
Self.MaskEdit2.MaxLength := 2;
Self.MaskEdit2.TabOrder := 1;
Self.MaskEdit2.Tag := 2;
Self.MaskEdit2.Text := '00';
Self.MaskEdit2.Top := 2;
Self.MaskEdit2.OnExit := MyOnExit;
Self.MaskEdit2.OnEnter := MyOnEnter;
Self.MaskEdit2.OnChange := MyOnChange;
Self.MaskEdit2.OnKeyDown := MyOnKeyDown;
Self.MaskEdit2.OnKeyPress := MyOnKeyPress;
Self.MaskEdit2.OnKeyUp := MyOnKeyUp;
Self.MaskEdit2.OnClick := MyOnClick;
Self.MaskEdit2.OnDblClick := MyOnDblClick;
Self.MaskEdit2.OnMouseDown := MyOnMouseDown;
Self.MaskEdit2.OnMouseMove := MyOnMouseMove;
Self.MaskEdit2.OnMouseUp := MyOnMouseUp;
Self.MaskEdit1 := TMaskEdit.Create(Self);
Self.MaskEdit1.Parent := Self;
Self.MaskEdit1.BorderStyle := bsNone;
Self.MaskEdit1.EditMask := '99;1;_';
Self.MaskEdit1.MaxLength := 2;
Self.MaskEdit1.TabOrder := 2;
Self.MaskEdit1.Tag := 3;
Self.MaskEdit1.Text := '00';
Self.MaskEdit1.Top := 2;
Self.MaskEdit1.OnExit := MyOnExit;
Self.MaskEdit1.OnEnter := MyOnEnter;
Self.MaskEdit1.OnChange := MyOnChange;
Self.MaskEdit1.OnKeyDown := MyOnKeyDown;
Self.MaskEdit1.OnKeyPress := MyOnKeyPress;
Self.MaskEdit1.OnKeyUp := MyOnKeyUp;
Self.MaskEdit1.OnClick := MyOnClick;
Self.MaskEdit1.OnDblClick := MyOnDblClick;
Self.MaskEdit1.OnMouseDown := MyOnMouseDown;
Self.MaskEdit1.OnMouseMove := MyOnMouseMove;
Self.MaskEdit1.OnMouseUp := MyOnMouseUp;
Self.FChangeTag := 0;
Self.AdjustWidthHeight;
Self.SetToCurrentTime;
Self.F_Author := 'Yousef Zalli, U3F.Zalli@GMail.Com, Tel = 09123780840';
FPairKeyDownWithUp := 0;
FLastDownKey := 0;
end;
{مقداري که الان در تايم اديت هست رو به فرم رشته اي همراه با کولون مي ده، مثل '12:01:01'}
function TU30TimeEdit.GetValueInColonStringFormat: string;
begin
Result := MaskEdit3.Text + ':' + MaskEdit2.Text + ':' + MaskEdit1.Text;
end;
{مقدار رو به فرم عددي مي ده، مثل 120101}
function TU30TimeEdit.GetValueInIntegerFormat: integer;
begin
Result := StrToInt(StringReplace(GetValueInColonStringFormat , ':', '', [rfReplaceAll]));
end;
{مقدار رو به فرم رشته بدون کولون مي ده مثل '120101'}
function TU30TimeEdit.GetValueInStringFormat: string;
begin
Result := StringReplace(GetValueInColonStringFormat, ':', '', [rfReplaceAll]);
end;
{در اينجا داريم هندلر خودمون رو پياده مي کنيم، اين هندلر همون طور که ديديم در هنگام تغيير هر کدوم از اديت ها کال مي شه}
procedure TU30TimeEdit.MyOnChange(Sender: TObject);
begin
if FChangeTag = 0 then
if Assigned(OnChange) then
Self.OnChange(Self);
end;
{اين هم مثل همونه، ولي مثلا در هنگام کليک ليبل ها هم کال مي شه}
procedure TU30TimeEdit.MyOnClick(Sender: TObject);
begin
if Assigned(Self.OnClick) then
Self.OnClick(Self);
end;
procedure TU30TimeEdit.MyOnDblClick(Sender: TObject);
begin
if Assigned(Self.OnDblClick) then
Self.OnDblClick(Self);
end;
{در اينجا مي خوايم بگيم که اگر رفت تو اديت، متن داخلش آبي بشه و انتخاب شه تا راحت عوض شه}
procedure TU30TimeEdit.MyOnEnter(Sender: TObject);
begin
{اين تگ مشخص مي کنه که در اين اديت هستيم، اضافيه و مي تونيد برش داريد}
(Sender as TMaskEdit).Tag := 1;
(Sender as TMaskEdit).SelectAll;
end;
{در هنگام خروج بايد ببينيد که کاربر هر دو رقم رو زده يا نه، اگر نزده يک صفر قبلش مي ذاريم}
procedure TU30TimeEdit.MyOnExit(Sender: TObject);
var
x: integer;
begin
(Sender as TMaskEdit).Text := RightStr('00' + trim((Sender as TMaskEdit).Text), 2);
x := StrToIntDef(trim((Sender as TMaskEdit).Text), 0);
{اعتبار اعدادي که زده شده تست مي شه و در صورت اشتباه بودن تصحيح مي شه}
if (Sender = Self.MaskEdit1) and ((x > 59) or (x < 0)) then
Self.MaskEdit1.Text := '01'
else if (Sender = Self.MaskEdit2) and ((x > 59) or (x < 0)) then
Self.MaskEdit2.Text := '01'
else if (Sender = Self.MaskEdit3) and ((x > 23) or (x < 0)) then
Self.MaskEdit3.Text := '01';
(Sender as TMaskEdit).Tag := 0;
end;
{اين متد خيلي مهمه و نکته داره، مثلا مي خواهيم عدد 12 رو بزنيم، خب، اول يک رو فشار مي ديم بعد 2 ديگه! اما اتفاقي که عملا در کار در سرعت بالا مي افته اينه که قبل از رها شدن دکمه 1 دکمه 2 فشرده مي شه، اگر اين کار رو هندل نکرده باشيد کامپوننت در اين وضعيت دچار مشکل مي شه.
براي اصلاح و کنترل اين وضع، بايد آمار فشرده شدن و رها شدن دکمه ها رو دستمون داشته باشيم تا ببينيم به ازاي هر فشرده شدن رها شدني هم انجام مي شه يا نه.
نکته ديگه اينه که قسمت اعداد بالا با قسمت اعداد نام پد در اين متدها کدهاي متفاوتي توليد مي کنه و بايد حواسمون به اين موضوع باشه}
procedure TU30TimeEdit.MyOnKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Assigned(Self.OnKeyDown) then
Self.OnKeyDown(Sender, Key, Shift);
{اينجا قسمتيه که چک مي کنيم ببينيم عددي فشرده شده يا نه، اگر عدد فشرده شده با آخرين عدد فشرده شده يکي نبود يعني کاربر قبل از رها کردن يک دکمه ديگه رو فشار داده}
if (Key in [48..57, VK_NUMPAD0..VK_NUMPAD9]) then
if FLastDownKey <> Key then
begin
inc(FPairKeyDownWithUp);
FLastDownKey := Key;
end;
end;
procedure TU30TimeEdit.MyOnKeyPress(Sender: TObject; var Key: Char);
begin
if Assigned(Self.OnKeyPress) then
Self.OnKeyPress(Sender, Key);
end;
{اينجا هم همين موضوع رو براي رها کردن دکمه ها داريم}
procedure TU30TimeEdit.MyOnKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
OnExitPtr: TNotifyEvent;
begin
if Assigned(Self.OnKeyUp) then
Self.OnKeyUp(Sender, Key, Shift);
if not (Key in [48..57, VK_NUMPAD0..VK_NUMPAD9]) then
Exit;
dec(FPairKeyDownWithUp);
if FPairKeyDownWithUp <> 0 then
Exit;
FLastDownKey := 0;
OnExitPtr := Self.OnExit;
Self.OnExit := nil;
{در اين قسمت چک مي کنم ببينيم لازمه که به اديت بعدي بپريم يا نه}
if Sender = Self.MaskEdit1 then
begin
if (Length(trim(Self.MaskEdit1.Text)) = 2) or (StrToIntDef(trim(Self.MaskEdit1.Text), 0) > 5) then
begin
MaskEdit1.OnExit(MaskEdit1);
MaskEdit3.OnEnter(MaskEdit3);
if not SameText(F_Author, '') then
{اين خيلي مهمه، هرگز از SetFocus خود دلفي در اين زمينه استفاده نکنيد، اين کار جلوي گرفتن AV رو در هنگام آماده نبودن فوکوس مي گيره}
Windows.SetFocus(MaskEdit3.Handle);
end;
end
else if Sender = Self.MaskEdit2 then
begin
if (Length(trim(Self.MaskEdit2.Text)) = 2) or (StrToIntDef(trim(Self.MaskEdit2.Text), 0) > 5) then
begin
MaskEdit2.OnExit(MaskEdit2);
MaskEdit1.OnEnter(MaskEdit1);
if not SameText(F_Author, '') then
Windows.SetFocus(MaskEdit1.Handle);
end;
end
else if Sender = Self.MaskEdit3 then
begin
if (Length(trim(Self.MaskEdit3.Text)) = 2) or (StrToIntDef(trim(Self.MaskEdit3.Text), 0) > 2) then
begin
MaskEdit3.OnExit(MaskEdit3);
MaskEdit2.OnEnter(MaskEdit2);
if not SameText(F_Author, '') then
Windows.SetFocus(MaskEdit2.Handle);
end;
end;
Self.OnExit := OnExitPtr;
end;
procedure TU30TimeEdit.MyOnMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Assigned(Self.OnMouseDown) then
Self.OnMouseDown(Self, Button, Shift, X, Y);
end;
procedure TU30TimeEdit.MyOnMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
if Assigned(Self.OnMouseMove) then
Self.OnMouseMove(Self, Shift, X, Y);
end;
procedure TU30TimeEdit.MyOnMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Assigned(Self.OnMouseUp) then
Self.OnMouseUp(Self, Button, Shift, X, Y);
end;
{اينجا زمان سيستم رو مي ديم به کامپوننت}
procedure TU30TimeEdit.SetToCurrentTime;
begin
SetValueInStringFormat(StringReplace(Time_CurrentT ime, ':', '', [rfReplaceAll]));
end;
procedure TU30TimeEdit.SetValueInColonStringFormat(const Value: string);
begin
SetValueInStringFormat(StringReplace(Value, ':', '', [rfReplaceAll]));
end;
procedure TU30TimeEdit.SetValueInIntegerFormat(const Value: integer);
begin
SetValueInStringFormat(RightStr('000000' + IntToStr(Value), 6));
end;
procedure TU30TimeEdit.SetValueInStringFormat(const Value: string);
begin
inc(FChangeTag);
Self.MaskEdit1.Text := Copy(Value, 5, 2);
Self.MaskEdit2.Text := Copy(Value, 3, 2);
Self.MaskEdit3.Text := Copy(Value, 1, 4);
BeforeExit;
dec(FChangeTag);
if FChangeTag = 0 then
if Assigned(OnChange) then
Self.OnChange(Self);
end;
{اين هم يک کلکه براي اينکه برنامه نويس در زمان طراحي نام نويسنده رو ببينه ولي نتونه اصلاحش کنه}
procedure TU30TimeEdit.Set_Author(const Value: string);
begin
//
end;
دوستان اگر مي بينيد که داره سخت مي شه دليلش اينه که بايد با آزمون و خطا مسايل پيش اومده رو مديريت کنيد. خود من 6 ماه بعد از به کار بردن کامپوننت هام، متوجه موضوع فشرده شدن دکمه ها شدم، يا مثلا فرق داشتن کد هاي اعداد، بنابراين با ديدن چند تا ارور مايوس نشيد و دست از کار نکشيد.
اميدوارم که عزيزان با نظر دادن من رو براي ادامه آموزش دلگرم کنند.
هر جا گنگ بود يا نياز به توضيح بيشتر داشت بفرماييد تا توضيح بدم.
فعلا تا جلسه ي بعدي..