PDA

مشاهده نسخه کامل : مشکل این کلاس کنترل در سی شارپ چیه؟



SajjadKhati
23-07-19, 13:46
سلام
یه کنترلی بنام TransparentControl را دارم میسازم (قبلا ساختم اما الان قابلیت های جدیدی دارم بهش اضافه میکنم)
توی نسخه ی قبل این جوری بود که اگه در سطح کنترل های خواهر و برادر این کنترل ، کنترلی به پشت این کنترل میرفت ، کاربر باید اون کنترل را میداد تا این کلاس ، قبل از رسم خودش ، اون کنترل را اول رسم کنه. این کلاس را در زیر پیوست میکنم
دارم این قابلیت را اضافه میکنم که نیازی نباشه که کاربر بخواد کنترل های پشت و کنترل های جلویِ (در سطح کنترل خواهر و برادری) کنترل TransparentControl را بهش بده و این کار را میخوام خود همین کنترل انجام بده . اما مشکلی داره که در زیر میگم .(هنوز بعضی از اعضاش کار نمیکنه و تموم نشد . فعلا از متد سازنده ای که کدش رو میذارم ، لطفا چک کنید) .
در متد سازنده ی فرم تون ، این را بنویسید لطفا (بجای مسیر فایل png ، که میدونین هر فایل مورد نظر خودتون که خواستین را بدین . فقط اندازه اش جوری باشه که دو کنترل در کد زیر ، نصف شون یا قسمتی ازشون ، روی همدیگه بیفتن) :



private TransparentControl transparentControl;
private TransparentControl transparentControl_2;




public Form1()
{
Bitmap bitmap = new Bitmap(@"E:\Project\Visual Studio\C#.Net\Saved Project\0 Important Project\Poshtibangir Tolo\PoshtibangirTolo\bin\Debug\Icon\PanelToolBar\ Close\Close.png");
TransparentControlBitmap controlBitmap = new TransparentControlBitmap(bitmap, new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height));
controlBitmap.GraphicInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseEnterBitmap, Color.DeepSkyBlue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseDownBitmap, Color.Blue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.ControlDisabledBitmap, Color.Yellow);
this.transparentControl = new TransparentControl(controlBitmap, new Point(200, 10));
this.transparentControl.Name = "Close";
this.transparentControl.MouseClick += new MouseEventHandler(this.TransparentControl_MouseCli ck);


Bitmap bitmap_2 = new Bitmap(@"E:\Project\Visual Studio\C#.Net\Saved Project\0 Important Project\Poshtibangir Tolo\PoshtibangirTolo\bin\Debug\Icon\PanelToolBar\ Setting\Setting.png");
TransparentControlBitmap controlBitmap_2 = new TransparentControlBitmap(bitmap_2);
controlBitmap_2.MouseEnterBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.DeepSkyBlue);
controlBitmap_2.MouseDownBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.Blue);
controlBitmap_2.ControlDisabledBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.Yellow);
this.transparentControl_2 = new TransparentControl(controlBitmap_2, new Point(210, 20));
this.transparentControl_2.Name = "Setting";
this.transparentControl_2.MouseClick += new MouseEventHandler(this.TransparentControl_MouseCli ck);


this.Controls.Add(transparentControl);
this.Controls.Add(transparentControl_2);
}

نکته ی عجیب برای من اینه که وقتی روی همین کد بالا (مخصوصا در خط this.Controls.Add(transparentControl) و همینطور در قسمت get و همینطور set برای پروپرتی BackgroundControls در کلاس زیر که پیوست کردم ، علامت breakpoint را میذارم و اگه بریک پوینت را همه را با دکمه ی F11 بزنم بره جلو ، پروپرتی BackgroundControls ، در شی transparentControl ، مقدار میگیره (اولین عضو از مقدارش برابر با شی transparentControl_2 میشه) که منم همین رو میخوام و درسته . اما هر دفعه بریک پوینت را با دکمه ی F10 بزنم بره جلو یا اصلا بریک پوینتی نذارم، با کمال تعجب میبینم که این پروپرتی (پروپرتی BackgroundControls ، در شی transparentControl) ، اون مقداری که قبلا گرفت را نداره و null میشه !!
چرا این دو اتفاق میافته؟
بسیار تعجب آوره !!



بعد اینکه میشه وقتی کسی کنترلی را BringToFront کرد ، ما بتونیم رویدادی ازش بدست بیاریم؟ یعنی رویدادی برای BringToFront ظاهرا وجود نداره . داره؟ اگه روشی برای این کار هست یا کاریش میشه کرد (از طریق api ویندوز و هر روش دیگه ای) ، میگین؟ منظورم کد نیست . در صورت وجود ، لطفارفرنس بدین ، خودم بررسی میکنم.

----------------------------------------------------------------

روند کلی کنترل TransparentControl رو اینجوری کردم که هر کنترلی در سطح خواهر و برادر کنترل TransparentControl مون (که والد یکسان دارن) ، به کنترل والدش اضافه شد یا تغییر باند داد (بجز کنترلِ جاری TransparentControl) ، به رویداد Parent_ControlAddedAndRemoved و SiblingControls_BoundsChanged متصل شون کردم . توی این رویدادها هم که هر دو یک کار را میکنن ، اول چک میکنم که کنترلی که این رویدادها براشون اتفاق افتاد ، آیا با کنترلِ جاری TransparentControl ، محیط مشترک (همون Rectangle.Intersect) دارن یا نه؟ اگه داشتن ، چک میکنم که آیا این کنترلِ اضافه شده ، یه بار در کلِ کنترل های پشتی اش هست؟ اگه بود ، اون کنترل رو به اعضای آرایه ی (پروپرتی) BackgroundControls اش اضافه میکنم . اگه در کنترل پشتی اش نبود ، در کل کنترل های جلویی اش چک میکنم که وجود داره یا نه و اگه اونجا وجود داشت ، اون کنترل رو به اعضای آرایه ی (پروپرتی) ForegroundControls اش اضافه میکنم .
بعد هم اعضای پروپرتی BackgroundControls رو قبل از Invalidate کردنِ کنترل جاری TransparentControl ام invalidate میکنم (فقط اون بخشی شون را که با کنترل TransparentControl ام مشترک هستند ، invalidate میکنم) (تا قبل از کنترل جاری TransparentControl ام رسم بشن) و همین کار را بعد از invalidate کردنِ کنترل جاری TransparentControl ام برای پروپرتی ForegroundControls انجام میدم (این را هم بخش مشترک شون را invalidate میکنم) ( تا بعد از کنترل جاری TransparentControl ام رسم بشن)

البته این قسمت از کدها فعلا یه مشکلی داره که باید وقتی متد OnParentChanged اجرا میشه هم ، این عملیات را یه بار انجام بدم یعنی رویداد Parent_ControlAddedAndRemoved را اون موقع ، فراخونی کنم.
مشکلات دیگه ای هم شاید داشته باشه (فعلا نمیدونم).
خیلی ممنون


دانلود فایل پیوست TransparentControl (Only the registered members can see the link)

(چون نمیذاشت توی اینجا فایلی پیوست کنم ، در سایت دیگه گذاشتم)

ravegoat
23-07-19, 15:27
سلام سجاد جان،

من کد شما رو داخل یه پروژه قرار دادم و اجرا کردم ولی مقدار BackgroundControls همیشه خالی بود. در متد Set این خاصیت هم Breakpoint گذاشتم ولی هرگز برنامه در این نقطه متوقف نشد! علاوه بر اون رویداد OnMouseEnter به همین دلیل خطا میده.

در مورد بخش دوم سوالت، منظورت رو از رویداد BringToFront متوجه نشدم!

فکر کنم لازم باشه بیش تر شرح بدی یا خود پروژه رو بذاری. این جوری شاید بشه بهتر راهنمایی کرد.

شاد باشی
آرمین

SajjadKhati
23-07-19, 22:09
سلام
خیلی ممنون استاد آرمین.
کد (پست اول) را بجای متد سازنده ، توی رویداد Load گذاشتم ، درست شد (و پروپرتی BackgroundControls در شی transparentControl در کد بالا ، که انتظار داشتم ، بدون بریک پوینت ای مقدار گرفت)
حالا دلیل اش را نمیدونم.
نمیدونم هم میشه داخل کلاس TransparentControl هم کدی نوشت که این محدودیت برداشته بشه.


منظورم متد Control.BringToFront هست . یه رویدادی میخوام که کسی برای کنترلش این متد را فراخونی میکنه ، اون رویداد را فراخونی کنیم . البته احتمالا بشه در اینجا یه چیزایی در این باره پیدا کرد . اما هنوز کامل چک نکردم :

WM_WINDOWPOSCHANGED message - Windows applications | Microsoft Docs (Only the registered members can see the link)

ravegoat
24-07-19, 11:06
خواهش می کنم سجاد جان!


کد (پست اول) را بجای متد سازنده ، توی رویداد Load گذاشتم ، درست شد (و پروپرتی BackgroundControls در شی transparentControl در کد بالا ، که انتظار داشتم ، بدون بریک پوینت ای مقدار گرفت)
حالا دلیل اش را نمیدونم.
در مورد این بخش، هنوز واسه من عجیبه که چنین رفتاری رو مشاهده می کنی! کلا یه تقدم و تاخر در اجرای constructor فرم و رویداد Load فرم وجود داره ولی همون طور که گفتم شاید بهتر باشه کل پروژه ات رو پیوست کنی (csproj به همراه sln) تا دقیق تر بررسی کرد.


نمیدونم هم میشه داخل کلاس TransparentControl هم کدی نوشت که این محدودیت برداشته بشه.
منظورت رو از محدودیت متوجه نمیشم.


منظورم متد Control.BringToFront هست . یه رویدادی میخوام که کسی برای کنترلش این متد را فراخونی میکنه ، اون رویداد را فراخونی کنیم . البته احتمالا بشه در اینجا یه چیزایی در این باره پیدا کرد
الان متوجه شدم. کلا اگر یه کنترل رو BringToFront کنی، رویداد Layout در container اون تحریک میشه. مثلا اگر بخوای label1 رو در form1 جلو تر از سایر کنترل ها قرار بدی، رویداد Layout در form1 فراخوانی میشه. اون پیوندی هم که گذاشتی نمی دونم چقدر می تونه کمکت کنه ولی غالب WinAPI ها به شکل Managed در دات نت وجود داره و فکر کنم اون موردی که شما قرار دادی بیش تر به درد تشخیص تغییر موقعیت یه پنجره بخوره.

شاد باشی
آرمین

SajjadKhati
24-07-19, 11:54
خواهش می کنم سجاد جان!


در مورد این بخش، هنوز واسه من عجیبه که چنین رفتاری رو مشاهده می کنی! کلا یه تقدم و تاخر در اجرای constructor فرم و رویداد Load فرم وجود داره ولی همون طور که گفتم شاید بهتر باشه کل پروژه ات رو پیوست کنی (csproj به همراه sln) تا دقیق تر بررسی کرد.


منظورت رو از محدودیت متوجه نمیشم.


خیلی ممنون
تقدم و تاخر اش را میدونم . ولی دلیل اش نسبت به کد خودم را متوجه نمیشم . نمیدونم چطور میشه که اون کد را توی متد سازنده که مینویسم ، اون پروپرتی BackgroundControls ام برای شی transparentControl (در اون کد) ، null میشه اما اون کد رو توی رویداد Load (که با تاخیر نسبت به متد سازنده اجرا میشه) که میذارم ، مشکلی نداره.
فرقی نداره پروژه ی sln را بدم یا نه . کلا کدش همون قدر به اضافه ی اون کلاس کنترل هست که پیوست کردم.

----------------------------------------------

الان یه مشکل دیگه داره . یه تغییراتی در این کلاس دادم که در زیر لینک دانلودش را میذارم :

دانلود فایل پیوست TransparentControl (Only the registered members can see the link)

با با اونکه پروپرتی BackgroundControls (شامل تمامی کنترل هایی که زیر کنترل جاری هست) را اول invalidate میکنم و بعد کنترل جاری را invalidate میکنم و بعد پروپرتی ForegroundControls (شامل تمامی کنترل هایی که جلوی کنترل جاری هست) را invalidate میکنم ، ولی برعکس رسم میکنه . یعنی کنترل های BackgroundControls را سر آخر رسم میکنه که باعث میشه جلو بیان .

در رویداد Load فرم هم همون کد پست اول را لطفا بنویسید (بجای مسیر فایل ها ، یه فایل png ای که بخشی اش transparent باشه ، بدید یا بیت مپی بدید که بخشی اش transparent باشه . اندازه ی بیت مپ هاتون جوری باشه که در کد زیر ، بخشی از هر دو شی از کنترل های transparentControl و transparentControl_2 روی هم بیفتن . در مثال زیر ، اندازه های شی بیت مپ ، 24 در 24 هستن ) :



private TransparentControl transparentControl;
private TransparentControl transparentControl_2;





private void Form1_Load(object sender, EventArgs e)
{
Bitmap bitmap = new Bitmap(@"E:\Project\Visual Studio\C#.Net\Saved Project\0 Important Project\Poshtibangir Tolo\PoshtibangirTolo\bin\Debug\Icon\PanelToolBar\ Close\Close.png");
bitmap = TransparentControlBitmap.SetColor(bitmap, Color.Red);
TransparentControlBitmap controlBitmap = new TransparentControlBitmap(bitmap, new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height));
controlBitmap.GraphicInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseEnterBitmap, Color.DeepSkyBlue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseDownBitmap, Color.Blue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.ControlDisabledBitmap, Color.Yellow);
this.transparentControl = new TransparentControl(controlBitmap, new Point(200, 10));
this.transparentControl.Name = "Close";
this.transparentControl.MouseClick += new MouseEventHandler(this.TransparentControl_MouseCli ck);




Bitmap bitmap_2 = new Bitmap(@"E:\Project\Visual Studio\C#.Net\Saved Project\0 Important Project\Poshtibangir Tolo\PoshtibangirTolo\bin\Debug\Icon\PanelToolBar\ Setting\Setting.png");
TransparentControlBitmap controlBitmap_2 = new TransparentControlBitmap(bitmap_2);
controlBitmap_2.MouseEnterBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.DeepSkyBlue);
controlBitmap_2.MouseDownBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.Blue);
controlBitmap_2.ControlDisabledBitmap = TransparentControlBitmap.SetColor(bitmap_2, Color.Yellow);
this.transparentControl_2 = new TransparentControl(controlBitmap_2, new Point(210, 20));
this.transparentControl_2.Name = "Setting";
this.transparentControl_2.MouseClick += new MouseEventHandler(this.TransparentControl_MouseCli ck);




this.Controls.Add(transparentControl);
this.Controls.Add(transparentControl_2);
}

هسته ی مرکزی کدهای کنترل کلاس TransparentControl که به این بخش مربوط میشه ، رویداد AllParents_Invalidated هست.
چرا این جوری هه؟
من که به ترتیب invalidate میکنم ولی این چرا BackgroundControls را سر آخر رسم میکنه؟






الان متوجه شدم. کلا اگر یه کنترل رو BringToFront کنی، رویداد Layout در container اون تحریک میشه. مثلا اگر بخوای label1 رو در form1 جلو تر از سایر کنترل ها قرار بدی، رویداد Layout در form1 فراخوانی میشه. اون پیوندی هم که گذاشتی نمی دونم چقدر می تونه کمکت کنه ولی غالب WinAPI ها به شکل Managed در دات نت وجود داره و فکر کنم اون موردی که شما قرار دادی بیش تر به درد تشخیص تغییر موقعیت یه پنجره بخوره.

شاد باشی
آرمین


خیلی ممنون

ravegoat
24-07-19, 18:32
الان متوجه سوال اولت شدم:
این مورد رو خیلی دقیق نمی دونم ولی وقتی فرم ساخته میشه الزاما نشون داده نمیشه ولی وقتی رویداد لود فرخوانی میشه حتما فرم به نمایش در اومده. وقتی تو کد مربوطه رو در constructor میذاری، اشاره گر مربوط به پنجره (handleNextControl) در کد زیر نا معتبره چون اصلا پنجره ای وجود نداره. پنجره زمانی معنا داره که فرم به نمایش در بیاد. در نتیجه در کد زیر مقدار false بر میگیرده و پیرو اون، خاصیت BackgroundControls مقدار دهی نمیشه:


handleNextControl = TransparentControl.GetWindow(handleNextControl, searchStyle);
if (handleNextControl == IntPtr.Zero)
return false;

اگر دقیق ترش رو میخوای باید ببینی در لایه ی پایین تابع ویندوزی GetWindow (Only the registered members can see the link) چه اتفاقی میافته.

در مورد سوال دوم ت بازم باید بگم متوجه نشدم. وقتی invalidate (Only the registered members can see the link amework-4.8) می کنی، کنترل پاک نمیشه بلکه در رویداد paint بعدی فرم، دوباره رسم میشه! اینکه میگی من invalidate می کنم ولی سر آخر رسم میشه اصلا برام واضح نیست. مگه invalidate کنی نباید رسم بشه؟! لطفا بیش تر شرح بده احتمالا بد متوجه شدم.

شاد باشی
آرمین:11():

SajjadKhati
24-07-19, 23:11
الان متوجه سوال اولت شدم:
این مورد رو خیلی دقیق نمی دونم ولی وقتی فرم ساخته میشه الزاما نشون داده نمیشه ولی وقتی رویداد لود فرخوانی میشه حتما فرم به نمایش در اومده. وقتی تو کد مربوطه رو در constructor میذاری، اشاره گر مربوط به پنجره (handleNextControl) در کد زیر نا معتبره چون اصلا پنجره ای وجود نداره. پنجره زمانی معنا داره که فرم به نمایش در بیاد. در نتیجه در کد زیر مقدار false بر میگیرده و پیرو اون، خاصیت BackgroundControls مقدار دهی نمیشه:



handleNextControl = TransparentControl.GetWindow(handleNextControl, searchStyle);
if (handleNextControl == IntPtr.Zero)
return false;



اگر دقیق ترش رو میخوای باید ببینی در لایه ی پایین تابع ویندوزی GetWindow چه اتفاقی میافته.


در مورد سوال دوم ت بازم باید بگم متوجه نشدم. وقتی invalidate می کنی، کنترل پاک نمیشه بلکه در رویداد paint بعدی فرم، دوباره رسم میشه! اینکه میگی من invalidate می کنم ولی سر آخر رسم میشه اصلا برام واضح نیست. مگه invalidate کنی نباید رسم بشه؟! لطفا بیش تر شرح بده احتمالا بد متوجه شدم.


شاد باشی
آرمین


خیلی ممنون استاد آرمین.
بله .
من فکر میکردم توی متد سازنده که کنترل مونو به کنترل کانتینری اضافه میکنیم ، پنجره تشکیل میشه.
نمیدونم برای کنترل های عادی دات نت مثل button و ... پس چجوری این قضیه ی رسم کنترل های زیرین و جلویی را پس مدیریت میکنن که توی متد سازنده هم ازشون شی بسازی ، مشکل مدیریت رسم کنترل های جلو و عقب شون را ندارن.

-----------------------------------

نگفتم invalidate کنیم ، پاک میشه . میدونم invalidate میکنیم ، رسم رایج را نامعتبر میکنه و باعث میشه رویداد paint اون کنترل ، (نه الزاما همون لحظه) اجرا بشه.
کد داخل تابع AllParents_Invalidated را لطفا ببینید.
نگفتم invalidate می کنم ولی سر آخر رسم میشه .
2 تا شی کنترل TransparentControl را پشت و جلوی هم میذارم (کد پست اول یا پنجم)
گفتم داخل اون تابع ، 3 تا کنترل را invalidate میکنم . اول ، تمامی کنترل های موجود در آرایه ی BackgroundControls (که شامل کنترل هایی هست که در پشت کنترل جاری TransparentControl مون هست)، بعدش کنترلِ جاری TransparentControl و بعدش مامی کنترل های موجود در آرایه ی ForegroundControls (که شامل کنترل هایی هست که در جلویِ کنترل جاری TransparentControl مون هست) را invalidate میکنم.
بریک پوینت که میذارم ، همه ی این موارد ، به همین ترتیب اجرا و invalidate میشن اما سر آخر علاوه بر این ترتیب 3 تایی که درست اجرا شدن ، دوباره اون کنترل های پروپرتی BackgroundControls مجددا رسم میشن که رسم مجدد اینها باعث میشه کنترل های پشتی ، به عنوان کنترل جلویی نمایش داده بشن . کد پست 5 را اجرا کنید ، مشخص هه.
نمیدونم این رسم آخریِ کنترل های پروپرتی BackgroundControls ، از کجا نشات میگیره.

ravegoat
25-07-19, 15:26
خیلی ممنون استاد آرمین.
بله .
من فکر میکردم توی متد سازنده که کنترل مونو به کنترل کانتینری اضافه میکنیم ، پنجره تشکیل میشه.
نمیدونم برای کنترل های عادی دات نت مثل button و ... پس چجوری این قضیه ی رسم کنترل های زیرین و جلویی را پس مدیریت میکنن که توی متد سازنده هم ازشون شی بسازی ، مشکل مدیریت رسم کنترل های جلو و عقب شون را ندارن.

-----------------------------------

نگفتم invalidate کنیم ، پاک میشه . میدونم invalidate میکنیم ، رسم رایج را نامعتبر میکنه و باعث میشه رویداد paint اون کنترل ، (نه الزاما همون لحظه) اجرا بشه.
کد داخل تابع AllParents_Invalidated را لطفا ببینید.
نگفتم invalidate می کنم ولی سر آخر رسم میشه .
2 تا شی کنترل TransparentControl را پشت و جلوی هم میذارم (کد پست اول یا پنجم)
گفتم داخل اون تابع ، 3 تا کنترل را invalidate میکنم . اول ، تمامی کنترل های موجود در آرایه ی BackgroundControls (که شامل کنترل هایی هست که در پشت کنترل جاری TransparentControl مون هست)، بعدش کنترلِ جاری TransparentControl و بعدش مامی کنترل های موجود در آرایه ی ForegroundControls (که شامل کنترل هایی هست که در جلویِ کنترل جاری TransparentControl مون هست) را invalidate میکنم.
بریک پوینت که میذارم ، همه ی این موارد ، به همین ترتیب اجرا و invalidate میشن اما سر آخر علاوه بر این ترتیب 3 تایی که درست اجرا شدن ، دوباره اون کنترل های پروپرتی BackgroundControls مجددا رسم میشن که رسم مجدد اینها باعث میشه کنترل های پشتی ، به عنوان کنترل جلویی نمایش داده بشن . کد پست 5 را اجرا کنید ، مشخص هه.
نمیدونم این رسم آخریِ کنترل های پروپرتی BackgroundControls ، از کجا نشات میگیره.
خواهش می کنم سجاد جان،
البته بازم میگم بنده استاد نیستم...

احتمالا وقتی در دات نت constructor به خط آخرش میرسه، CLR توابع گرافیکی رو اجرا می کنه که پنجره رسم بشه و بعد از اونه که ویندوز فرم رو به عنوان یه پنجره میشناسه. در سطح سی شارپ ما به دستورات بین آخرین خط Constructor و اولین خط رویداد Form_Load دسترسی نداریم ولی این قضیه تداخلی با مدیریت ترسیم کنترل ها روی فرم نداره. هر کنترلی داخل container یه z-order داره و هر container ی داخل فرم هم یک z-order مختص به خودش رو داره. ترتیب پیش فرض در z-order به ترتیب اضافه شدن اون کنترل داخل container بر می گرده. اگر نگاهی به تابع InitializeComponent در یه فرم بندازیم، کنترل ها به ترتیبی رسم میشن که داخل InitializeComponent به فرم اضافه شدن. به عبارتی پس از InitializeComponent، توابع JIT به هر کنترل یه z-order تخصیص میدن و در نهایت CLR به کارت گرافیک هندسه ی ترسیم رو ارسال می کنه.

-----------------------------------

سوال دوم ت رو هم الان متوجه شدم. وقتی یه کنترل رو invalid کنیم، تا زمانی که فرم idle نشه، اون کنترل paint نمیشه. حالا این که فرم کی idle میشه رو نمی دونم ولی اینجا فعلا اهمیتی نداره. ولی نکته اینه که اگر دو تا کنترل رو به ترتیب invalid کنیم، الزاما در زمان idle بودن فرم به همون ترتیب رسم نمیشن! در کدی که قرار دادی، transparetControl_2 اول و بعدش transparetControl1 نا معتبر میشه که درسته. ولی در ادامه اول transparetControl1 و بعدش transparetControl_2 رسم میشه. اگر بخوای بلافاصله یه کنترل رو رسم کنی می تونی از دستورات Update و Refresh استفاده کنی ولی شاید باز هم به هدف ت نرسی. اگر هدف اینه وقتی کاربر روی کنترل پشتی با اشاره گر ماوس وایستاد، اون کنترل بیاد جلو، فکر کنم در کد اولی که گذاشته بودی این اتفاق می افتاد. یه جورایی واسم گنگه که دقیقا میخوای چیکار کنی چون متوجه نشدم چرا یه نسخه ی جدید تر از کد اول قرار دادی! مشکل کد اول چی بوده؟!

شاد باشی
آرمین :11():

SajjadKhati
27-07-19, 14:16
خیلی ممنون استاد آرمین.
نکته ی مهم اینه که وقتی کنترلی را Invalidate میکنیم ، زمانی اجرا میشه که پیام (رویداد و هر کدی) توی صف برای پردازش نباشه. اگه باشه ، اول اونا را اجرا میکنه و بعد نوبت به Invalidate اون کنترل میرسه (که اول اون کنترل را ، نامعتبر ، و بعد رسم اش میکنه) . پس اشکال کار من توی این سه خط ، در تابع AllParents_Invalidated بود :



if (this.Equals(TransparentControl.TranspControlEvent DoingFlag) == true)
{ this.InvalidateForeOrBackgroundControls(this.Backg roundControls, this.BackgroundControlInvalidateRegiones);

this.Invalidate();

this.InvalidateForeOrBackgroundControls(this.Foreg roundControls, this.ForeGroundControlInvalidateRegiones);
}

پس باید هر کدوم از این خط ها که میخواد اجرا بشه (و Invalidate کنترل مورد نظر را در پی داره) ، باید زمانی اجرا بشه که مطمئن بشیم اون خط قبلی ، رسم اش تمام شد (نه صرفا با فراخونی Invalidate ، رسم میشه همونطور که قبلا توضیح دادین) و بعد خط بعدی شو اجرا کنیم.
این 3 خط را این طور ویرایش کردم که خط اول را همونجا گذاشتم باشه . بعدش تایمری را استارت کردم و خط دوم (this.Invalidate) را توی تایمر گذاشتم باشه (چون اگه اشتباه نکنم ، تایمر هم مثل Invalidate ، زمانی اجرا میشه که پیام (رویداد و هر کدی) توی صف برای پردازش نباشه. پس در این صورت ، مطمئنیم که اول ، خط اول ، رسم اش تمام شد و بعد نوبت تایمر مون میرسه .
اما خط سوم را دیگه نمیشه توی تایمر گذاشت چون دو تایمر ممکنه زمان اجراشون (طی مدت زمانی که میگذره یا به هر دلیل دیگه) ، تداخل پیدا کنن . بنابراین کد سوم را توی متد OnPaint (بعد از کدهای رسم مربوط به کنترل TransparentControl ، میذاریم) و حالا مطمئن هستیم که بعد از رسم کنترل TransparentControl ، عملیات Invalidate مربوط به کنترل های جلویی انجام میشه.



این کار (کد بالا که آخرین تغییرات کلاسش را در زیر هم پیوست میکنم) را انجام دادم . در این صورت (با این تغییراتی که گفتم) ، اگه دو کنترلِ TransparentControl را جلو و عقب هم بذاریم ، جواب میده و کنترل های جلو و عقب ، درست رسم میشه (البته بجز یه اشکال کوچیک که اگه اول ، موس را روی کنترل پشتی ببریم (موس را دیگه از اون کنترل ، خارج نکنیم) و از همونجا ، موس را روی کنترلِ جلویی ببریم ، ترتیب رسم ، باز به هم میریزه که با خارج کردن موس از همین کنترلِ جلویی ، دوباره درست میشه . حالت های دیگه ، مشکل خاصی انگار نبود) .
اما در حالتی که پشت کنترلِ TransparentControl مون ، یه کنترل دیگه مثل Button باشه ، با اونکه کنترل TransparentControl مون جلو هست ، ولی پشت اش رسم میشه.

برای تست این قضیه ، کد زیر را در رویداد Form_Load بنویسین (همونطور که میدونین ، بجای مسیر فایل ها ، یه فایل png ای که بخشی اش transparent باشه ، بدید یا بیت مپی بدید که بخشی اش transparent باشه . اندازه ی بیت مپ هاتون جوری باشه که در کد زیر ، بخشی از هر دو شی از کنترل های transparentControl و button روی هم بیفتن . در مثال زیر ، اندازه های شی بیت مپ ، 24 در 24 هستن) :



private TransparentControl transparentControl;




private void Form1_Load(object sender, EventArgs e)
{
Bitmap bitmap = new Bitmap(@"E:\Project\Visual Studio\C#.Net\Saved Project\0 Important Project\Poshtibangir Tolo\PoshtibangirTolo\bin\Debug\Icon\PanelToolBar\ Close\Close.png");
bitmap = TransparentControlBitmap.SetColor(bitmap, Color.Red);
TransparentControlBitmap controlBitmap = new TransparentControlBitmap(bitmap, new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height), new Bitmap(bitmap.Width, bitmap.Height));
controlBitmap.GraphicInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseEnterBitmap, Color.DeepSkyBlue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.MouseDownBitmap, Color.Blue);
controlBitmap.SetColor(bitmap, ExportBitmapFor.ControlDisabledBitmap, Color.Yellow);
this.transparentControl = new TransparentControl(controlBitmap, new Point(200, 10));
this.transparentControl.Text = "Close";
this.Controls.Add(transparentControl);


Button button = new Button() { BackColor = Color.GreenYellow, Bounds = new Rectangle(210, 20, 80, 35), Text = "Back Button", Parent = this };
button.BringToFront();
}

الان این مشکل از کجاست که با اونکه شی transparentControl ، در جلوی کنترل button قرار داره (وقتی موس را روی کنترل transparentControl میبریم) ، اما جلوش رسم نمیشه؟


فایل پیوستی شامل آخرین تغییرات را هم بی زحمت از اینجا دانلود کنید. (Only the registered members can see the link)


خیلی ممنون :give_rose:

SajjadKhati
28-07-19, 11:05
سلامی مجدد
چقدر پارانویید میزنم :Love-ssa~! (1):
کد (در پست بالا) مشکلی نداره .
بخاطر اینکه در خط آخر ، اونو جلو آوردم ( کد button.BringToFront ) ، بود . حواسم نبود .
خیلی ممنون

SajjadKhati
22-08-19, 20:14
سلامی مجدد
آخرین تغییراتی که دادم را فایل این کلاس را در زیر میذارم.
الان مشکلش اینه که وقتی پروپرتی TextAutoSize از کلاس TransparentControl را تغییر میدم ، متن ای که رسم میکنه ، وقتی موس را روی این کنترل میبریم ، ضخیم رسم میکنه . دلیل شو گشتم ، ولی پیدا نکردم . کسی میدونه راهنمایی کنه؟

الان این کد زیر ، اشکالی که در بالا توضیح دادم را نمایان میکنه (اول روی دکمه ی AutoSize ، کلیک راست کنید و به متن کنترل TransparentControl هم دقت کنید که خواهید دید کمی ضخیم تر میشه . اگه فرم را مینیمایز و بعد به حالت اولیه اش برگردونید ، متن ، نازک میشه. اگه دوباره این کاری که گفتم را انجام بدین یا موس را روی کنترل ببرید ، متن ، ضخیم تر میشه) :



private void Form1_Load(object sender, EventArgs e)
{
Button btnAutoSize = new Button { Bounds = new Rectangle(10, 10, 100, 35), Parent = this, Text = "AutoSize", BackColor = Color.WhiteSmoke };
btnAutoSize.MouseUp += new MouseEventHandler(this.Button3_MouseUp);


TransparentControlText text = new TransparentControlText("salam");
this.transparentControl = new TransparentControl(text, new Rectangle(new Point(100, 200), new Size(250, 100)), false);
this.Controls.Add(this.transparentControl);
}


private void Button3_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.transparentControl.TextAutoSize = false;
else if (e.Button == MouseButtons.Right)
this.transparentControl.TextAutoSize = true;
}






دانلود آخرین تغییرات فایل TransparentControl :

Only the registered members can see the link

با تشکر :give_rose:

SajjadKhati
23-08-19, 10:18
سلامی مجدد
انگار حدودا ، بهتر پیدا کردم که مشکل از کجاست (اما هنوز دقیق متوجه نشدم) .
مشکل انگار از کدهای تغییر سایز در TransparentControl هست . ربطی به TransparentControlText نداره.
مثلا کدهای زیر ، مشکلی ندارن (این کد ، رویداد Button3_MouseUp در کد بالاست که تغییر کرده) (برای آپدیت و رسم ، بعد از کلیک روی دکمه ، موس را روی کنترل TransparentControl ببرید) :




private void Button3_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.transparentControl.TransparentControlText.Tex t = "salam";
else if (e.Button == MouseButtons.Right)
this.transparentControl.TransparentControlText.Tex t = "khobi?";
}


یا



private void Button3_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.transparentControl.TransparentControlText.Tex tFont = this.Font;
else if (e.Button == MouseButtons.Right)
this.transparentControl.TransparentControlText.Tex t = "khobi?";
}

اما این کد مشکل داره :



private void Button3_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.transparentControl.Size = new Size(250, 100);
else if (e.Button == MouseButtons.Right)
this.transparentControl.Size = new Size(180, 70);
}

حالا داخل کلاس TransparentControlText مشکل خودش را نشون میده چون داخل رویداد TransparentControl_TextAutoSizeChanged ، کدِ this.TransparentControl.Size = fontSize.ToSize() را بکار بردم.

SajjadKhati
23-08-19, 14:22
سلامی مجدد
مشکل یافت شد. بخاطر این بود که داخل رویداد AllParents_Invalidated ، وقتی جابجایی یا تغییر اندازه صورت میگرفت ، متد InvalidateBackgroundsAndThisControl ، دو بار اجرا میشد. چون هر بار که این تابع اجرا میشه ، به کنترل والدش کاری نداره و کنترل والدش را invalidate نمیکنه.
بنابراین ، کد این رویداد را به زیر تغییر دادم و درست شد :




private void AllParents_Invalidated(object sender, InvalidateEventArgs e)
{
// در این قسمت ، عمل Invalidate کردن یا همون رسم کردن کنترل TransparentControl را فقط روی اون کنترلی انجام میده که عملیات موس روی اون کنترل انجام گرفته باشه .
// همچنین اگه مکان کنترل جاری مون تغییر کنه ، همون کنترل رسم میشه.
if (this.Equals(TransparentControl.TranspControlEvent DoingFlag) == true || this.IsLocationChangeDoingFlag == true)
this.InvalidateBackgroundsAndThisControl();
}