ورود

View Full Version : سوال: آیا راهی برای غیر فعال کردن موقتی Invalidate در لیست باکس هست؟



mbshareat
چهارشنبه 25 خرداد 1390, 01:01 صبح
با سلام و خدا قوت خدمت بزرگان:قلب:
من تو برنامم یه لیست باکس دارم که ترسیم نوشته های درونش رو خودم به عهده گرفتم.:لبخند:
من تو برنامم کلیدهایی رو تعریف کردم که در محتوای آیتم جاری لیست باکس تغییر ایجاد می کنند.
از اونجا که لیست من نوشته هایی که که در محیط داس تایپ شدند ترسیم می کنه ترسیم کند عمل می کنه و روند ترسیم قابل مشاهده است.(مثلا دو دهم ثانیه برای یک صفحه):متعجب:
برای ترسیم یک پروسیجر دارم که مقدار آیتم رو میخونه و کارکترها رو به معادل ویندوزی تغییر میده.
من می خوام تنها سطر جاری رو تغییر مقدا بدم ولی با این کار کل لیست باکس ترسیم میشه و لرزشی تو تصویر دیده میشه(چون به یک باره کل لیست پاک میشه و مجددا ترسیم میشه).:عصبانی:
آیا راهی هست که بتونم از ترسیم تمام آیتمها جلو گیری کنم.
تصور من اینه که تغییر مقدار یک آیتم Invalidate رو فراخوانی میکنه.:لبخندساده:
اگه راهی نباشه مجبورم به جای تغییر مقدار آیتم جاری لیست باکس برای ترسیم آیتمها یک مقدار یک TSringList داشته باشم که در DrawItem مقدار رو از اون بگیرم و وقتی میخوام تنها یک آیتم را تغییر بدم در همین StringList تغییر ایجاد کنم و پروسیجر ترسیم آیتم رو فراخوانی کنم.این روش شاید جواب بده ولی لازمه اش تغییر اساسی تو کدمه.:گیج:
اگه کسی راهی سراغ داره لطفا راهنمایی کنه!:متفکر:
لطفا اگه راه

tdkhakpur
چهارشنبه 25 خرداد 1390, 10:52 صبح
یا راهی هست که بتونم از ترسیم تمام آیتمها جلو گیری کنم.


هر كنترلي كه ساخته ميشه اگه دقت كنيد يك پراپرتي به عنوان owne داره كه شما بهش مقدار true‌بديد تا اين كنترل وفقه هاي ساختش رو به رويداد ondrawitem ببره اونجا ميتونيد اين فيلتر رو اعمال كنيد كه فقط اون آيتمي كه مد نظر داريد ترسيم بشه.
البته يه سري جزئيات داره كه بايد كنترلي هم داشته باشيد مثل ترسيم مجدد خود ليست باكس يا paint‌ يا earase كه اساسا تغييرات برنامه ها در حين اجراي داخل desktop باعث اجراي اين رويداد ميشه و كل ليست باكس بايد ترسيم بشه.

mbshareat
چهارشنبه 25 خرداد 1390, 15:15 عصر
سلام و عیدتون مبارک
دوست گرامی مگه نمی دونین من چقدر مبتدی هستم!
نمی دونم جوابتون به سوالم ربط داشت یانه. من عرض کردم که ترسیم رو خودم به عهده گرفتم و این کار رو با پروسیجر انجام میدم و مشکلم پاک شدن تمام آیتمهاست که ترسیم مجدد تمامشون رو ضروری میکنه.در ضمن تو نسخه دلفی من این پروپرتی برای لیست باکس نیست؛مگر اینکه منظورتون پارامتر AOwner برای Create باشه که خدتون میدونین من اصلا Create رو به عهده نگرفتم.در ضمن این پارامتر چه ربطی به جلوگیری از Invalidate داره؟
ببخشید از روی نادونی در برابر شما اظهار نظر کردم.اگه ممکنه یه نمونه کدی، توضیح بیشتری چیزی لطف کنین.

SAASTN
چهارشنبه 25 خرداد 1390, 19:00 عصر
سلام، راستش من از توضیحاتتون چیز زیادی دستگیرم نشد.

من تو برنامم یه لیست باکس دارم که ترسیم نوشته های درونش رو خودم به عهده گرفتم.
این دقیقا یعنی چی؟ دارید با DrawItem خود TListBox کار می کنید؟ یه کلاس از TListBox مشتق کردید؟ اصلا یه کلاس از CustomControl یا بالاتر مشتق کردید؟ اگر کامپوننت نویسی کردید Paint یا Invalidate رو override کردید؟
ببینید اگر می خواید یه کلاس مثلا Invalidate نشه تصورم اینه که کامپوننت باید بازنویسی بشه، یه فلگ به کامپوننت اضافه بشه Invalidate هم override بشه و در اون فلگ بررسی بشه و در صورت مثلا True بودنش inherited فراخونی بشه. حالا هرجا خواستید کامپوننت دیگه Invalidate نشه اون فلگ رو False می کنید.

mbshareat
چهارشنبه 25 خرداد 1390, 23:44 عصر
سلام وعیدتون مبارک
بله من از DrawItem استفاده کردم و درون او یه پروسیجر فراخوانی می کنم که ترسیم رو با TextOut انجام می ده.
به نظر میرسه تعریف کلاسی از TLisyBox که تنها Invalidate اون آنطور که گفتین باشه سخت نباشه. اگه ممکنه کد زیر رو نگاهی بیندازین ببینین چرا اشکال می گیره که

Class TListBox not found


unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TListX = class(TListBox)
private
constructor Create(AOwner: TComponent); override;
Procedure Invalidate;override;
public
CanDrawAll:Boolean;
End;
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TListX.Create(AOwner: TComponent);
begin
inherited;
CanDrawAll:=True;
end;
procedure TlistX.Invalidate;
begin
If CanDrawAll Then
Perform(CM_INVALIDATE, 0, 0);
end;
end.

در ضمن وقتی روی عبارت TlistBox در کد بالا Ctrl+Click می کنم، خطا میده که:

Error inpecting 'TListBox': Symbol was eliminated by linker

SAASTN
پنج شنبه 26 خرداد 1390, 01:05 صبح
والا چی بگم! کد بدون مشکل اجرا میشه و Ctrl+Click هم درست هدایت میکنه. می خوای یه بار همه از ماشین پیاده شن دوباره سوارشن، شاید درست بشه:لبخند:

در ضمن وقتی روی عبارت TlistBox در کد بالا Ctrl+Click می کنم، خطا میده که:
این خطارم من در همچین وضعیتی باهاش برخورد نکردم. این خطا معمولا وقتی بوجود میاد که کد در وضعیت Optimized کامپایل شده باشه و موقع دیباگ کردن برنامه روی یک شناسه که از نظر کامپایلر دیگه در روند اجرای برنامه کاربرد نداره Shift+Ctrl+Click کنیم (فشردن این ترکیب منجر به باز شدن پنجره Debug Inspector به ازای اون شناسه میشه).
حالا اینکه چرا قبل از اجرا، اونهم با Ctrl+Click این خطا رو میده، دیگه من نمی دونم.

mbshareat
پنج شنبه 26 خرداد 1390, 01:58 صبح
سلام و شب بخیر
من حواسم نبود که یه لیست باکس تو فرم بود ولی تعریفش تو کلاس فرم نبود از روی فرم برداشتمش همه چیز درست شد.
می بخشید AOwner در Create چه چیزی باید باشه. من تو Help چیزی پیدا نکردم.
در ضمن نمی دونم چرا مختصات و Parent رو قبل از Create قبول نمی کنه.
من با این کد تونستم لیست باکس سفارشی خودم رو بسازم.لطفا بفرمایین اشکال نداره؟

procedure TForm1.FormCreate(Sender: TObject);
begin
LL:=TListX.Create(nil);
LL.SetBounds(10,10,100,100);
LL.Parent:=self;
LL.Items.Text:='a'+#10+'b'+#10+'c';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
lL.CanDrawAll:=False;
LL.Items[0]:='aaa';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
lL.CanDrawAll:=True;
LL.Items[1]:='bbb';
end;

فقط یه مساله مهم هست Button1 رو که فشار می دم باز آیتم ترسیم میشه!
فکر کنم گیر کار جای دیگه ای باشه.احتمالا مجبور شم از همون روش دومم استفاده کنم(STringList رو تغییر بدم نه Items از لیست باکس)

SAASTN
پنج شنبه 26 خرداد 1390, 12:09 عصر
در ضمن نمی دونم چرا مختصات و Parent رو قبل از Create قبول نمی کنه.
خوب قبل از Create که حافظه مربوط به شیئ هنوز تخصیص داده نشده، در واقع شیئ هنوز وجود نداره که بخواد Parent یا مختصات داشته باشه.

من با این کد تونستم لیست باکس سفارشی خودم رو بسازم.لطفا بفرمایین اشکال نداره؟
من مشکلی توش نمی بینم.

فقط یه مساله مهم هست Button1 رو که فشار می دم باز آیتم ترسیم میشه!
ببینید من توی پست قبلی فقط گفتم که چطور میشه از Validate شدن تایع چلوگیری کرد. ولی خوب معلوم نیست ترسیم کنترل تنها از اون مسیر انجام بشه.
برای اینکه بدونید تغییر در Items چطور باعث ترسیم مجدد ListBox میشه یه نگاهی به کلاس TListBoxStrings در یونیت StdCtrls بندازید. برای تغییر عملکردش شاید مجبور شید به نوعی خود TCustomListBox از اول بنویسید که عملیست بسیار مشقت بار!
اما Override کردن توابع DrawItem و CNDrawItem ازترسیم مجدد جلوگیری می کنه، ولی خوب این هم احتمالا مد نظر شما نیست، چون باعث میشه هیچ کدوم از آیتم ها ترسیم نشن. در واقع TCustomListBox یطوری نوشته شده که به محض تغییر یکی از آیتم هاش، DrawItem رو به ازای همه آیتم ها فراخونی می کنه. ولی خوب به هر صورت یه نگاهی بهش بندازید:
TMyListBox = class(TListBox)
private
FCanDrawItem: Boolean;
protected
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
override;
public
constructor Create(AOwner: TComponent); override;
published
property CanDrawItem: Boolean read FCanDrawItem write FCanDrawItem;
end;

{ TMyListBox }

procedure TMyListBox.CNDrawItem(var Message: TWMDrawItem);
begin
if FCanDrawItem then
inherited;
end;

constructor TMyListBox.Create(AOwner: TComponent);
begin
inherited;
FCanDrawItem := True;
end;

procedure TMyListBox.DrawItem(Index: Integer; Rect: TRect;
State: TOwnerDrawState);
begin
if FCanDrawItem then
inherited DrawItem(Index, Rect, State);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
LL.CanDrawItem := False;
LL.Items[0] := '1';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
LL.CanDrawItem := True;
LL.Items[1] := '2';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
LL := TMyListBox.Create(Self);
with LL do
begin
Parent := Self;
OnDrawItem := ListBox1.OnDrawItem;
Style := lbOwnerDrawFixed;
Items.Add('Item1');
Items.Add('Item2');
end;
end;

procedure MyDrawItem(Canvas: TCanvas; Rect: TRect; Selected: Boolean;
Text: string);
const
Colors: array[Boolean] of TColor = (clYellow, clWhite);
begin
Canvas.Brush.Color := Colors[Selected];
Canvas.FillRect(Rect);
Canvas.Font.Color:=clBlack;
Canvas.TextOut(Rect.Left + 2, Rect.Top, Text);
end;

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with TListBox(Control) do
MyDrawItem(Canvas, ItemRect(Index), odSelected in State, Items[Index]);
Sleep(100);
end;


فکر کنم گیر کار جای دیگه ای باشه.احتمالا مجبور شم از همون روش دومم استفاده کنم(STringList رو تغییر بدم نه Items از لیست باکس)
تو چه مقاطعی و از چه طریقی می خواید آیتم ها رو ترسیم کنید؟
شاید بهتر باشه بیشتر روی پائین آوردن زمان ترسیم کار کنید.

mbshareat
پنج شنبه 26 خرداد 1390, 23:42 عصر
سلام بر همه دوستان
عیدتون مبارک
از لطفتون ممنونم.من چنین دستوری رو دیده بودم و قاطی کرده بودم:
with TButton.Create(Form) do
چون چیز زیادی از کلاس و شیوه دستکاری کلاس نمی دونم می ترسم اقدام کنم.به همون روشی که اجمالا اشاره کردم تغییر زیادی تو کدم دادم و موفق هم شدم ولی کد شما رو هم می خونم.

تو چه مقاطعی و از چه طریقی می خواید آیتم ها رو ترسیم کنید؟
برای ترسیم با استفاده از DrawItem میشه محتوای لیست باکس رو ترسیم کرد که باعث میشه با تغییر تنها یک آیتم،لیست پاک شده و تمام آیتمهای قابل مشاهده لیست از اول ترسیم بشن.
اما ا گه یه متغیر داشته باشیم از نوع TStringList که حاوی همون عناصر لیست باکس باشه هنگام نغییر یک آیتم از این متغیر دیگه DrawItem فراخوانی نمی شه.اما در هر صورت تعداد آیتمها در متغیر مزبور و تعداد آیتمهای لیست باکس باید یکسان باشه.و باید پروسیجری خارج از DrawItem باشه که هنگام تغییر یک آیتم فراخوانی بشه تا تنها اون آیتم رو ترسیم کنه.این پرو سیجر میتونه در DrawItem هم برای ترسیم عموم آیتمها استفاده بشه.
فکر کنم همینقدر توضیح کافی باشه.
در مورد کد شما یه چیز رو نفهمیدم:
چرا هم در CNDrawItem و هم در TMyListBox.DrawItem مقدار FCanDrawItem رو بررسی کنیم.
اگه درست فهمیده باشم CNDrawItem قبل از TMyListBox.DrawItem اجرا میشه و اگه شرط برقرار نباشه TMyListBox.DrawItem نباید اجرا بشه.به نظر میرسه هرکدوم به تنهایی کافی باشن.
در هر صورت با محبتتون شرمندم فرمودین.خدا حفظتون کنه.التماس دعا..

SAASTN
جمعه 27 خرداد 1390, 22:46 عصر
می بخشید AOwner در Create چه چیزی باید باشه. من تو Help چیزی پیدا نکردم.
آقا شرمنده، من می خواستم راجع به AOwner تو پست قبلی بگم که یادم رفت.:اشتباه: حافظه در حد کلم!
این پارامتر درواقع مشخصه Owner یک TComponent رو تعیین می کنه. Owner یک کامپوننت دو وظیفه داره، اول این که موقع آزادسازی خودش، کامپوننت های متعلق به خودش رو هم آزاد می کنه. و دوم اینکه مشخصه های با سطح دسترسی pubished کامپوننت های متعلق به خودش رو ذخیره و بازیابی می کنه.
Owner هر کامپوننتی که در زمان Design رو فرم میذاریم برابر خود فرم قرار می گیره که باعث میشه تمام کامپوننت ها موقع آزاد شدن فرم به همراهش آزاد بشن. از طرفی موقعی که یه فرم داره تو حافظه لود میشه تمام کامپوننت های متعلق به خودش رو هم لود میکنه.
اینا مربوط میشدن به کلیت Owner، حالا صحبت بر سر پارامتر AOwner هست، یعنی این که ما Owner کامپوننت هایی که در زمان اجرا ایجاد کردیم رو چی تعیین کنیم. خوب بازم بهتره که Owner کامپوننت هایی هم که در زمان اجرا ایجاد می کنیم برابر فرمی قرار بدیم که کامپوننت قراره درش دیده بشه(البته اگه کامپوننت دیداری باشه). این مسئله دوتا مزیت داره، اول این که موقعی که فرم داره از بین میره، خودش بطور خودکار، حافظه مربوط به کامپوننت ما رو هم آزاد میکنه. دوم این که کامپوننتی که ایجاد کردیم از طریق مشخصه Components فرم قابل دستیابی میشه. برای درک بهتر این که این موارد چطور کار میکنند متدهای Create و Destroy مربوط به TComponent رو در یونیت Classes بررسی کنید.
برای مشخص شدن تفاوت Owner با Parent هم این لینک (http://delphi.about.com/od/objectpascalide/a/owner_parent.htm) رو ببینید.


with TButton.Create(Form) do
در مورد این کد هم بگم که Create یک constructor یا سازنده یک نمونه از کلاس TButton هست. با این که constructor ها در موقع تعریف شبیه به روالها تعریف میشن و مانند توابع خروجی برای اونها تعیین نمیشه، اما در واقع اونها مثل یک تابع عمل می کنند و یک ارجاع به نمونه ایجاد شده رو در خروجی قرار میدن.
کد بالا یه نمونه از کلاس TButton رو ایجاد میکنه و عبارت with-do هم به این نمونه اشاره می کنه. یعنی اگه کد مثلا به شکل زیر باشه:
with TButton.Create(Form1) do
Width := 100;

اون Wdith که در خط دوم اومده مربوط به TButton ی هست که همین الان ایجاد شده.
حالا اینکه چرا این کد به اینصورت نوشته میشه، علتش اینه که برنامه نویس در طول اجرای برنامه دیگه به دسترسی به این دکمه احتیاج نداشته، یعنی همین که ایجادش میکرده و مشخصاتش رو مقداردهی اولیه میکرده کفایت میکرده و لازم نبوده بعدا در یه روال دیگه مثلا یه مشخصه دیگش رو تغییر بده. از بین رفتن دکمه هم که با توجه به توضیحات قبلی به عهده Form1 قرار داده شده.
اما اگه به این دکمه در روالهای دیگه هم احتیاج داشته باشیم، باید مثلا یه متغیر در فرم تعریف کنیم و نمونه ایجاد شده رو در این متغیر قرار بدیم تا جاهای دیگه هم بهش دسترسی داشته باشیم:
MyButton := TButton.Create(Form1);
MyButton.Width := 100;


در مورد کد شما یه چیز رو نفهمیدم:
چرا هم در CNDrawItem و هم در TMyListBox.DrawItem مقدار FCanDrawItem رو بررسی کنیم.
اگه درست فهمیده باشم CNDrawItem قبل از TMyListBox.DrawItem اجرا میشه و اگه شرط برقرار نباشه TMyListBox.DrawItem نباید اجرا بشه.به نظر میرسه هرکدوم به تنهایی کافی باشن.
کاملا درسته، چون DrawItem خودش در CNDrawItem فراخونی میشه و اگه CNDrawItem اجرا نشه تبعا DrawItem هم اجرا نخواهد شد. مسئله این بود که من اول روی DrawItem کار کردم که دیدم اگه اجرا هم نشه باز محل آیتم سفید میشه، بعد رفتم ببینم که قبل از فراخونی DrawItem دیگه کجاها با Canvas کار شده که CNDrawItem رو دیدم. بعد اون رو غیر فعال کردم که دیدم در نتیجه تفاوتی ایجاد نشد. موقعی که می خواستم کد رو قرار بدم هیج کدوم رو حذف نکردم تا کسی که کد رو می بینه با هردو روال آشنا بشه. اما در واقع در کد قبلی override کردن DrawItem لزومی نداره.