خیلی ممنون استاد از توضیح مفصل تون.
خوب منم همین رو میگم . مثالش Font هست . خیلی از کنترل هایی که شرکت های دیگه هم میزنن ، هست و ... .
شما همیشه میگین چرا دو تا کلاس مجزای دیگه زدم.
یه چیزهایی متوجه شدم . اما دقیقا متوجه نشدم باید چی کار کنم؟ روی چه کلاس و چه پروپرتی ای ، چه اتریباتس (یا اتریباتس هایی) و چه کارهای اضافه تری باید انجام بدم؟
اول مطمئن بشید که کدتون خطای نهفته نداره. در قدم بعدی برای کلاس TransparentControlBitmap تون باید کاری انجام بدید که مثل Font قابل بازتولید باشه.
براتون Font رو مثال زدم، خوب منطقی است که قبل از اینکه ابهام پیش بیاد باید کد Font رو ببینید، FontConverter اش رو ببینید.
این EditorAttributes را الان بجای اینکه روی پروپرتی TransparentControl.TransparentControlBitmap قرار بدم ، باید روی کلاس TransparentControl قرار بدم؟
کلا یه کم بیشتر راهنمایی کنین.
باید نیست، اختیاریه. هر دو جا هم می توانید Editor رو مشخص کنید، برای مشخصه و برای کلاس. می توانید هر دو رو انجام بدید یا یکی شون رو. اگر قرار باشه TransparentControlBitmap به اندازه Font در کلاس ها و مشخصه های مختلفی بکار بره بهتره که خود کلاس Editor اش رو مشخص کرده باشه، وگرنه وقتی کارکردش در یکی دو مشخصه بیشتر نباشه فرقی نمی کنه که مشخصه Editor رو تعیین کنه یا کلاس، مثل کلاس شما.
همونطور که میدونید ، قصد فروش این کنترل رو دارم .
برای اینکه سورس اش در دسترس عموم قرار نگیره ، بجز پیام خصوصی ، من به چه روشی میتونم به دست تون برسونم؟
توی پیام خصوصی ، من سئوالات را مطرح نکردم . فقط فایل رو آپلود کردم . سئوال را اینجا مطرح کردم.
نیازی نیست برای نشون دادن یک مشکل کد کنترل خودتون رو قرار بدید، یک مثال کوچیک و مستقل برای کدی که مشکل ایجاد میکنه بسازید، سریعترین راه برای پیدا کردن دلیل مشکل همینه که کد رو خلوت کنید. مثلا الان اینکه کنترل رسمش Transparent ئه یا دو تا کلاس جانبی داره یا کلاس ها چند تا مشخصه دارند یا رسم اش چطوریه که مهم نیست، شما یک کلاس کنترل ساده بدون مشخصه های اضافی لازم دارید که فقط یک کلاس جانبی داره برای یک مشخصه که داخل اون کلاس هم فقط یک مشخصه Bitmap داره که اونم با Editor تعیین میشه که وقتی تغییر کرد کنترل به ساده ترین شکل ممکن رسم اش تغییری می کنه، همین. مشکلش رو که پیدا کردید و حل شد، تغییرات لازم رو در پروژه اصلی تون می دهید. چیزی که لازمه پیوست کنید همچین پروژه مختصر ئه که ربطی هم به کنترل شما و کارکردش نداره.
توقع داشتم همون کدی که من نوشتم را بنویسه دیگه .
یعنی TransparentControlBitmap.DefaultBitmap = new Bitmap("file address") را بذاره.
چطور چنین چیزی ممکنه، شما داخل DefaultBitmap اون "file address" رو قرار نمیدید، Bitmap هم که مشخصه ای برای مسیر Path نداره که بشه از رویش کد ساخت، تازه اگر داشت هم شما فایل exe پروژه تون که نباید به مسیر "file address" وابسته باشه.
به هر حال . پس الان مشکل از resources.GetObject نیست . در اونجا ، مقدار اون فایل تصویر ، بصورت باینری ذخیره میشه و خونده میشه. (توی فایل Resources.resx ذخیره میشه؟)
من الان چه کدی را باید چک کنم؟ کلا اون قسمت از کد Editor ام که باعث میشه بیت مپی جدید را اختصاص بده ، 2 خط بیشتر نیست :
کد:
if (btnBrowseImage.Equals(this.btnBrowseDefaultBitmap) == true)
{
this.CurrentTransparentControlBitmap.DefaultBitmap = new Bitmap(this.browseFileDialog.FileName);
this.transparentDefaultBitmap.TransparentControlBitmap.DefaultBitmap = this.CurrentTransparentControlBitmap.DefaultBitmap;
}
CurrentTransparentControlBitmap هم شی جاری و برابر با پروپرتیِ TransparentControl.TransparentControlBitmap هست که بصورت سراسری تعریف شده. شی browseFileDialog هم از نوع OpenFileDialog هست.
وقتی هم اجرا میشه ، توی دیزاینر ، کد به درستی اجرا میشه و تغییراتش دیده میشه (حتی شی transparentControl ام که روی فرم هست ، بیت مپِ مورد نظرش تغییر میکنه و دیده میشه) اما وقتی که بخوام برنامه را اجرا کنم (دکمه ی start و f5 را میزنم) ، برنامه که اجرا میشه ، این تغییرات دیده نمیشه و شی transparentControl ام دوباره به حالت پیش فرض خودش (وقتی که متد سازنده ی پیش فرض را اجرا کردم) تبدیل میشه و تغییر شکل میده.
من کجای این دو خط کد را بررسی کنم؟
کلا یه کم راهنمایی بیشتر کنین . اگه زحمت تون نیست ، اون فایلی که توی پیام خصوصی براتون آپلود کردم هم دانلود کنین ، ممنون میشم.
اول یک پروژه مستقل و ساده رو شروع کنید، متد های سازنده پارامتر دار رو اصلا در این پروژه جدید استفاده نکنید، خلاصه ترین حالت ممکن. یک کلاس برای کنترل تون بسازید که یک مشخصه به اسم TransparentControlBitmap داره، بعد کلاس TransparentControlBitmap رو تعریف کنید که متد سازنده اش پارامتر نداره و فقط یک مشخصه DefaultBitmap داره که وقتی تغییر کرد باید در کنترل رسم بشه. بعد یک Editor برایش بسازید. حالا در تمامی روتین های این پروژه که خیلی کوچیک شده با try catch و Breakpoint وضعیت خطا رو بررسی کنید. بعد که مطمئن شدید خطایی نداره، برای TransparentControlBitmap صفت
Serializable رو تعریف کنید و اگر دلتون میخواد می توانید ShouldSerialize
DefaultBitmap رو هم برای مشخصه DefaultBitmap بنویسید که فقط وقتی true برگردونه که مشخصه مقدار null نداشته باشه.
یه سئوال اینکه چرا وقتی یه کنترل TransparentControl را از toolbox به فرم (در دیزاینر) اضافه میکنم ، توی form1.designer.cs ، تقریبا همه ی پروپرتی هایی که دادم از هر 3 کلاسی که نوشتم را مقداردهی میکنه؟ حتی مقدار null را هم به پروپرتی ها اختصاص میده! نال که دیگه نال هه بصورت پیش فرض. مقداردهی کردن نمیخواد .
چیکار باید کنم که بهش بگم لازم نیست فلان پروپرتی ها را مقداردهی نکنی در دیزاینر؟
چون Designer که نمیدونه کدوم لازمه و کدوم لازم نیست. پیشفرض بودن به این معنی نیست که ذخیره اش نکن. پیشفرض به این معنی است که Reset شدنی هست یا نه. اینکه null یا پیشفرض ئه دلیل بر این نیست که نباید ذخیره بشه.
در مورد متد ShouldSerialize
PropertyName گفتم. برای مشخصه هایی که ShouldSerialize
PropertyName ندارند فرض بر اینه که همیشه مقدارشون مهم ئه و باید ذخیره بشن.
کلا چند جور انتخاب داریم :
1- کلاسی است که کلا نمیخواهیم Serialize بشه، یعنی نمیخواهیم ذخیره بشه که کدی خاص و صفتی هم لازم نداره. کنترل ها جزو این گروه نیستند، چون با صفت DesignerSerializer براشون Designer ذخیره سازی رو انجام میده.
2- در سایر حالات کلاسی داریم که میخواهیم Serialize بشه ولی با شرایط مختلف، که صفت
Serializable رو بهش میدیم. همچین کلاسی بصورت پیشفرض فیلد های داخلش همگی ذخیره میشه ولی برای اینکه روی serialization کنترل داشته باشیم چند انتخاب داریم :
2a- یک انتخاب اینه که بیاییم با ISerializable یک روال اختصاصی serialization بسازیم که در جای دیگری نیازی به تعریف شرایط برای مشخصه ها نباشه. مثل کاری که Font کرده.
2b- یک انتخاب دیگه اینه که بیاییم بگیم فلان فیلد داخلش رو هیچوقت serialize نکن (صفت
NonSerialized) که صرفا بقیه رو ذخیره کنه یا با ShouldSerialize
PropertyName برای ذخیره کردنش اما و اگر بذاریم. اگه تعداد فیلد هایی که ذخیره نمیشن زیاد باشه یا ذخیره کردن مقادیر شرط پیچیده داشته باشه بهتره بجای 2b از روش 2a استفاده کنید که در یک متد یکجا همه تصمیم گیری ها انجام بشه.
هم 2a و هم 2b باید صفت
Serializable رو داشته باشن، مگر اینکه مثل کنترل ها serialization جزو خصوصیات به ارث رسیده شون باشه.