گفتگو هایی در باب سی شارپ

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بله. اون Invalidate ها با همون ترتیب اجرای کدتون اعمال میشه، یک نخ که بیشتر نیست. با همون ترتیب که درخواست Invalidate شده Invalidate میشن. اما همانطور که قبلا هم گفتم و در توضیحات InvalidateRect هم نوشته، پیام WM_PAINT چیزی نیست که اولا همیشه و با هر Invalidate ای ارسال بشه و ثانیا اگه پیامش در صف اجرا قرار گرفت فورا نوبت اش برسه. هر پنجره ای هم صف پیام های مجزایی داره.
براتون با کد و مثال هم نشون دادم که ارتباط مستقیمی بین ایندو نیست.

چند بار در مورد WS_EX_TRANSPARENT گفتم و گوش نکردید.
#1176
#1137

خیلی ممنون.
خوب اون مگه invalidate اگه برای یه کنترل ، چند بار پی در پی اجرا کنیم ، ویندوز یا کدهای (درون متد Invalidate) در سی شارپ تصمیم میگیرن یه بارش را فقط ارسال کنن ولی ما که برای هر کنترل ، یه invalidate را ارسال میکنیم . هر کنترل که ربطی به یکی دیگه نداره.


ملاک درستی برای مقایسه قرار ندادید.
شما کنترل خودتون رو Invalidate میکنید و بعد کنترل دیگری رو که از هر نوعی میتونه باشه.
اگر اون کنترل بعدی یک کنترل عادی مثل button باشه که برایش هیچ فرقی نمی کنه که کنترل زیرش شفاف ئه یا نه. مهم نیست که رسم زیرش با تاخیر انجام بشه یا نه. مهم نیست اصلا رسمی زیرش بشه یا نه. پس اینکه روی کنترل تون button قرار میدید و درست عمل میکنه نشونه این نیست که کنترل شما رسمش درسته و با رسم روی خودش مشکلی نداره. دلیلش صرفا اینه که هر پنجره عادی که روی کنترل شما قرار بگیره درست دیده میشه. دکمه ای که پنجره اش بالاتر ازدکمه شما است که هیچوقت زیر پنجره کنترل شما نمیره. کنترل های زیرین هم بهشون درخواست Invalidate میدید. صف پیام هاشون هم خالیه و به موقع رسم میکنند. میمونه کنترل خودتون که رسمش مشکل داره.

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



شما در یک رخداد کنترل خودتون ، میایید درخواست میدید برای رسم. این رسم کی میتونه شروع بشه؟ وقتی پیام قبلی پردازش شده و پیام دیگری در صف جلوتر از اون نیست. شما مادامی که در اون روتین رخداد فلان هستید که رسم مجبوره تو صف بمونه. این انتظار برای سایر کنترل ها مهم نیست چون خیلی کوتاهه و به رسم سایر پنجره ها کاری ندارند. به سادگی Invalidate میکنند و از روتین رخداد فرضا کلیک خارج میشن و رسم انجام میشه. ولی کنترل شما میخواد توی اون رخداد که منجر به تغییر ظاهر شده بعد از رسم خودش سایر کنترل ها رو هم وادار به رسم کنه. رسم خودش به ناچار منتظر میمونه و رسم سایر کنترل ها که صف انتظارشون خالی است جلوتر از خودش انجام میشه.


به خوبی که احتمالا هیچوقت نمیشه ولی من جای شما باشم در AllParents_Invalidated صرفا تا Invalidate کنترل خودتون پیش میرم و اون بخش رسم کنترل های جلویی رو از اونجا Cut میکنم و برمیدارم و منتقل میکنم به انتهای OnPaint، یعنی وقتی کنترل های جلویی رو Invalidate میکنید که کنترل خودتون Paint شده، نه وقتی که تازه درخواست Paint کردید.

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

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
استاد ، یه کم درباره ی رویداد Invalidated و Paint (اینکه هر کدوم چند بار و دقیقا چه زمانی اجرا میشن) ، توضیح میدین؟ (یا در صورت امکان ، میگین مشکل پست قبلی را چجوری حل کنم؟)
آیا رویداد Invalidated ، مثلا 100 بار هم که پشت سر هم متد Control.Invalidate را صداش کنیم ، فقط یکبار رویدادش اجرا میشه؟ (همون کدی که قبلا دادین و یه خیلی متد Control.Invalidate را اجرا کرده بودین و رویدادش یه بار فقط فراخونی شده بود). اگه این طوره ، پس برای دفعه ی بعدی ، چه زمانی متد Control.Invalidate را صدا کنیم ، برای بار بعدی هم رویداد Invalidated اجرا میشه؟ (متوجه ی منظورم شدید؟)
چرا تعداد اجرای رویداد Invalidated و Paint با هم فرق دارن؟ آخه مگه نباید هر بار متد Control.Invalidate را فراخونی میکنیم ، به همون تعدادی که فراخونی میکنیم ، رسم (Paint فراخونی بشه) انجام بده ؟ چرا رویداد Paint اکثر اوقات دو یا چند بار اتفاق میافته؟ در این صورت (که دو یا چند بار اتفاق میافته) و ما فقط یکبار متد Control.Invalidate را فراخوانی کرده بودیم ، بقیه ی بارها ، کدوم متد باعث شد که این رویداد Paint اجرا بشه؟

اینا درستن؟ :
رویداد Paint هم که بعد از رویداد Invalidated اجرا میشه.
هر کنترلی که متد Control.Invalidate فراخونی بشه و کلا بخواد رسم بشه (مثلا کنترل Button) ، رویداد Paint والدش هم اجرا میشه . به عبارتی ، والدش را هم Invalidate میکنه.
خیلی ممنون :rose:
 

the_king

مدیرکل انجمن
خیلی ممنون.
خوب اون مگه invalidate اگه برای یه کنترل ، چند بار پی در پی اجرا کنیم ، ویندوز یا کدهای (درون متد Invalidate) در سی شارپ تصمیم میگیرن یه بارش را فقط ارسال کنن ولی ما که برای هر کنترل ، یه invalidate را ارسال میکنیم . هر کنترل که ربطی به یکی دیگه نداره.
ارسال Invalidate با رخداد Paint دو مورد متفاوته. اگر کنترلی به دلیلی در صف درخواست Paint باشه، فرضا بخاطر اینکه کنترل پشتی یا روی اون رسمی انجام داده، دیگه Invalidate کردن شما کار خاصی انجام نمیده. این مورد رو برای این گفتم که این مساله رو همیشه در نظر بگیرید، منظورم این نبوده که بگم شما بیش از یکبار Invalidate میکنید. و در ضمن شما نمی توانید مطمئن باشید که چون اول button1 رو Invalidate کردید و بعد button2 رو، پس حتما button1 زودتر رسم خودش رو انجام میده.
چرا مهمه رسم هاش.
فرض کنید یه دکمه ی button ، پشت کنترل TransparentControl باشه . اگه رسم ها ، به همون ترتیبی که من میگم مثل الان رعایت نشه و مثلا اول کنترل TransparentControl رسم بشه و بعد رسم Button انجام بشه ، باعث میشه کنترل Button که لایه ی زیری بود ، خودشو جلوتر نمایش بده و همین مشکلی که من با دو کنترل TransparentControl روی هم دارم ، پیش بیاد.
از پاسختون این نتیجه رو میگیرم.
شما یک دکمه پشت کنترل تون دارید، فرضا button1 که در BackgroundControls داریدش. بعد از روی فرم حذف میشه. و شما دیگه روی فرم نداریدش. و هر بار که میخواهید کنترل خودتون رو رسم کنید اون دکمه رو هم که دیگه نیست رو هم رسم می کنید. چرا؟ چون اون رسمش براتون مهمه، ازش خاطرات خوشی دارید و نمیخواهید حتی با حذف شدنش از مجموعه BackgroundControls خارج بشه.

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

the_king

مدیرکل انجمن
استاد ، یه کم درباره ی رویداد Invalidated و Paint (اینکه هر کدوم چند بار و دقیقا چه زمانی اجرا میشن) ، توضیح میدین؟ (یا در صورت امکان ، میگین مشکل پست قبلی را چجوری حل کنم؟)
آیا رویداد Invalidated ، مثلا 100 بار هم که پشت سر هم متد Control.Invalidate را صداش کنیم ، فقط یکبار رویدادش اجرا میشه؟ (همون کدی که قبلا دادین و یه خیلی متد Control.Invalidate را اجرا کرده بودین و رویدادش یه بار فقط فراخونی شده بود). اگه این طوره ، پس برای دفعه ی بعدی ، چه زمانی متد Control.Invalidate را صدا کنیم ، برای بار بعدی هم رویداد Invalidated اجرا میشه؟ (متوجه ی منظورم شدید؟)
چرا تعداد اجرای رویداد Invalidated و Paint با هم فرق دارن؟ آخه مگه نباید هر بار متد Control.Invalidate را فراخونی میکنیم ، به همون تعدادی که فراخونی میکنیم ، رسم (Paint فراخونی بشه) انجام بده ؟ چرا رویداد Paint اکثر اوقات دو یا چند بار اتفاق میافته؟ در این صورت (که دو یا چند بار اتفاق میافته) و ما فقط یکبار متد Control.Invalidate را فراخوانی کرده بودیم ، بقیه ی بارها ، کدوم متد باعث شد که این رویداد Paint اجرا بشه؟

خیلی ساده است. Invalidate میگه کل ناحیه پنجره یا صرفا بخشی از اون معتبر نیست. صد بار هم اعلام کنید که معتبر نیست عدم اعتبار شدیدتر یا غلیظ تر که نمیشه.
و اعتبار چی رو باطل کردید؟ رسم قبلی. پس این عدم اعتبار فقط تا وقتی معنی داره که یا رسم بعدی صورت بگیره یا برعکسش Validate بشه.
به محض اینکه رسم بعدی صورت بگیره دیگه Invalidate های تا اون لحظه همگی میرن پی کارشون و دیگه ناحیه نامعتبری باقی نمیمونه.
پنجره موقع رسم فقط میبینه کجاها معتبر نیست تا رسم مجدد شون کنه. کاری نداره که اون نقطه چند بار شامل Invalidate شده یا کی درخواستش رو داده. یک بار و صد بار فرقی نداره.
شما وقتی با Invalidate های متوالی چندین رسم انجام میدید که بین شون مجالی برای رخداد رسم باشه. یعنی هم نخ سرش شلوغ نباشه و هم پیام دیگری در صف انتظار نباشه. فرضا در button1_MouseDown دکمه button1 رو Invalidate میکنید ولی در حالت عادی وقتی رسم انجام میشه که از button1_MouseDown خارج شدید.

اینا درستن؟ :
رویداد Paint هم که بعد از رویداد Invalidated اجرا میشه.
هر کنترلی که متد Control.Invalidate فراخونی بشه و کلا بخواد رسم بشه (مثلا کنترل Button) ، رویداد Paint والدش هم اجرا میشه . به عبارتی ، والدش را هم Invalidate میکنه.
خیلی ممنون :rose:
نه. ربطی به هم ندارن. Paint الزاما نتیجه Invalidate نیست. Paint درخواست رسم به هر دلیلی است. توضیحات رو هم اگه میخوندید نوشته بود که اگر بعد از Invalidate یک Validate ای بی اثرش کنه دیگه چیزی برای رسم در Paint نمیمونه.
این گزاره ها رو بر چه مبنایی میگید که حالا درست و اشتباه بودنشون مطرح باشه؟ گزاره حتی اشتباه هم که باشه باید یک مبنای منطقی تجربی داشته باشه که بشه مطرحش کرد.
از کسی که با ویژوال استدیو آشنایی داره و میتونه با کد نویسی روابط رو بررسی کنه بعیده همینطوری یک چیزی بپرونه:
کد:
        private Timer _timer;
        private Control _c;
        private ListBox _listbox;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Height = 550;
            _listbox = new ListBox() { Bounds = new Rectangle(10, 80, 200, 400), Parent = this };
            _c = new Control() { BackColor = Color.LightBlue, Bounds = new Rectangle(10, 10, 50, 50), Parent = this };
            _c.Paint += c_Paint;
            _c.Invalidated += c_Invalidated;
            _timer = new Timer() { Interval = 1000, Enabled = true };
            _timer.Tick += timer_Tick;
        }

        private void c_Invalidated(object sender, InvalidateEventArgs e)
        {
            _listbox.Items.Add("c.Invalidate");
            _listbox.TopIndex = _listbox.Items.Count - 1;
        }

        protected override void OnInvalidated(InvalidateEventArgs e)
        {
            if (_listbox != null)
            {
                _listbox.Items.Add("form1.Invalidate");
                _listbox.TopIndex = _listbox.Items.Count - 1;
            };
            base.OnInvalidated(e);
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            _c.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            _listbox.Items.Add("form1.Paint");
            _listbox.TopIndex = _listbox.Items.Count - 1;
        }

        private void c_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(_c.BackColor);
            e.Graphics.DrawString(_listbox.Items.Count.ToString(), Font, Brushes.Black, Point.Empty);
            _listbox.Items.Add("c.Paint");
            _listbox.TopIndex = _listbox.Items.Count - 1;
        }
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
آها خیلی ممنون استاد علی
پس نکته ی مهم اینه که وقتی کنترلی را Invalidate میکنیم ، زمانی اجرا میشه که پیام (رویداد و هر کدی) توی صف برای پردازش نباشه. اگه باشه ، اول اونا را اجرا میکنه و بعد نوبت به Invalidate اون کنترل میرسه (که اول اون کنترل را ، نامعتبر ، و بعد رسم اش میکنه) . پس اشکال کار من توی این سه خط ، در تابع AllParents_Invalidated بود :


کد:
            if (this.Equals(TransparentControl.TranspControlEventDoingFlag) == true)
            {        this.InvalidateForeOrBackgroundControls(this.BackgroundControls, this.BackgroundControlInvalidateRegiones);

                this.Invalidate();

                this.InvalidateForeOrBackgroundControls(this.ForegroundControls, this.ForeGroundControlInvalidateRegiones);
            }

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

خیلی ممنون استاد علی . این کار (کد بالا که آخرین تغییرات کلاسش را در زیر هم پیوست میکنم) را انجام دادم . در این صورت (با این تغییراتی که گفتم) ، اگه دو کنترلِ TransparentControl را جلو و عقب هم بذاریم ، جواب میده و کنترل های جلو و عقب ، درست رسم میشهه (البته بجز یه اشکال کوچیک که اگه اول ، موس را روی کنترل پشتی ببریم (موس را دیگه از اون کنترل ، خارج نکنیم) و از همونجا ، موس را روی کنترلِ جلویی ببریم ، ترتیب رسم ، باز به هم میریزه که با خارج کردن موس از همین کنترلِ جلویی ، دوباره درست میشه . حالت های دیگه ، مشکل خاصی انگار نبود) . اما در حالتی که پشت کنترلِ TransparentControl مون ، یه کنترل دیگه مثل Button باشه ، با اونکه کنترل TransparentControl مون جلو هست ، ولی پشت اش رسم میشه (تست این قضیه ، نیاز به پروژه نداره استاد علی . الان شما همین کلاس 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 میبریم) ، اما جلوش رسم نمیشه؟
فایل پیوستی را هم بی زحمت از زیر دانلود کنید.
خیلی ممنون :rose:
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بعد اینکه استاد علی ، راهی هست که یک عضوی که در کلاس والد (از لحاظ شی گرایی و ارث بری منظورم هست) تعریف شده هست ، کاری کنیم که دیگه برنامه نویس نتونه اون عضو را وقتی شی ای از کلاس مشتق و فرزندش میسازه ، براش در دسترس نباشه؟
من توی همین کنترل TransparentControl ، اگه بشه ، نیاز دارم کاربر وقتی شی ای از این کلاس TransparentControl میسازه ، دیگه به عضو Text (که در کلاس پدرش یعنی کلاس Control تعریف شد) دسترسی نداشته باشه . یعنی موقع برنامه نویسی ای که طرف داره انجام میده ، این عضو ، در لیست اینتل لایسنس اش نیاد.
یا میشه کاری کرد که سطح دسترسی اعضای override ای که در کلاس فرزند انجام میدیم را تغییر بدیم؟ مثلا با اتریباتس خاصی و یا روش خاصس برای این کار نیست؟ (اگه اتریباتس BrowsableAttribute ، فقط از پنجره ی پروپرتیس ، این گزینه را غیر فعال کنه ، پس منظورم این اتریباتس هم نیست)
member hiding منظورم نیست.

بعد هم اینکه بی زحمت ، پست بالا (1285) را جواب میدین؟
خیلی ممنون
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
چقدر پارانویید میزنم :green:
کد (در پست 1285) مشکلی نداره .
بخاطر اینکه در خط آخر ، اونو جلو آوردم ( کد button.BringToFront ) ، بود . حواسم نبود .

سئوالم در پست بالا (1286) هم راه حلی نداره . درسته؟
خیلی ممنون
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
استاد علی ، میگم راهی وجود نداره که عملگر را داخل یه متغییر ذخیره کنیم . درسته؟
میخوام تابع زیر را جوری بنویسم که یکبار فقط نوشته بشه (یه بار در شرط doConnectEvent == true رویدادها را متصل نکنه و یه بار در شرط doConnectEvent == false رویدادها را قطع و disconnect نکنه و فقط یکبار نوشته بشه که عملگر مشخص کنه که این رویداد متصل بشه یا نه) اما انگار نشدنی هست . درست میگم؟ :


کد:
        private void InitializeEventForBackAndForegroundControls(Control controlParent, bool doConnectEvent)
        {
            if (doConnectEvent == true)
            {
                controlParent.ControlAdded += new ControlEventHandler(this.Parent_ControlAdded);
                foreach (Control siblingControl in controlParent.Controls)
                {
                    if (siblingControl.Equals(this) == false)
                    {
                        siblingControl.LocationChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.ClientSizeChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.SizeChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                    }
                }
            }
            else
            {
                controlParent.ControlAdded -= new ControlEventHandler(this.Parent_ControlAdded);
                foreach (Control siblingControl in controlParent.Controls)
                {
                    if (siblingControl.Equals(this) == false)
                    {
                        siblingControl.LocationChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.ClientSizeChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.SizeChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                    }
                }
            }
        }
 

the_king

مدیرکل انجمن
بعد اینکه استاد علی ، راهی هست که یک عضوی که در کلاس والد (از لحاظ شی گرایی و ارث بری منظورم هست) تعریف شده هست ، کاری کنیم که دیگه برنامه نویس نتونه اون عضو را وقتی شی ای از کلاس مشتق و فرزندش میسازه ، براش در دسترس نباشه؟
من توی همین کنترل TransparentControl ، اگه بشه ، نیاز دارم کاربر وقتی شی ای از این کلاس TransparentControl میسازه ، دیگه به عضو Text (که در کلاس پدرش یعنی کلاس Control تعریف شد) دسترسی نداشته باشه . یعنی موقع برنامه نویسی ای که طرف داره انجام میده ، این عضو ، در لیست اینتل لایسنس اش نیاد.
یا میشه کاری کرد که سطح دسترسی اعضای override ای که در کلاس فرزند انجام میدیم را تغییر بدیم؟ مثلا با اتریباتس خاصی و یا روش خاصس برای این کار نیست؟ (اگه اتریباتس BrowsableAttribute ، فقط از پنجره ی پروپرتیس ، این گزینه را غیر فعال کنه ، پس منظورم این اتریباتس هم نیست)
member hiding منظورم نیست.
کاری که انجام میشه و خود NET. هم استفاده کرده، تعریف یک مورد جدید با همون نام ئه (کلمه کلیدی new لازم داره). مثلا برای PictureBox مشخصه TabStop معنی نداره، چون از اون کنترل هایی است که نه میتونه Focus بگیره و نه مثل Label ها Focus رو به کنترل بعدی هدایت کنه، به همین دلیل در کد کلاس PictureBox مشخصه Control.TabStop رو از دید خارج می کنند تا PictureBox.TabStop ای به چشم نیاد. فرقی نمی کنه که اون مورد که مخفی می کنید override پذیر بوده یا نه. توجه داشته باشید که این کار برای جلوگیری از نمایش در محیط ویژوال استدیو است که Browsable و EditorBrowsable رو به رسمیت میشناسه، وگرنه قابلیت های System.Reflection برای دسترسی فراتر از اینجور پنهان سازی ها است.
کد:
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new bool TabStop
        {
            get
            {
                return base.TabStop;
            }
            set
            {
                base.TabStop = value;
            }
        }
و طبعا شما می توانید برای مورد new خودتون سطح دسترسی جدیدی در نظر بگیرید، اما اگر با اون سطح دسترسی فرضا private از خارج کلاس پنهان اش کنید دیگه نمیتونه موردی که در والدش public ئه رو پشت خودش پنهان کنه. برای همینه که TabStop ئه public تعریف شده.
 

the_king

مدیرکل انجمن
سلامی مجدد
استاد علی ، میگم راهی وجود نداره که عملگر را داخل یه متغییر ذخیره کنیم . درسته؟
میخوام تابع زیر را جوری بنویسم که یکبار فقط نوشته بشه (یه بار در شرط doConnectEvent == true رویدادها را متصل نکنه و یه بار در شرط doConnectEvent == false رویدادها را قطع و disconnect نکنه و فقط یکبار نوشته بشه که عملگر مشخص کنه که این رویداد متصل بشه یا نه) اما انگار نشدنی هست . درست میگم؟ :


کد:
        private void InitializeEventForBackAndForegroundControls(Control controlParent, bool doConnectEvent)
        {
            if (doConnectEvent == true)
            {
                controlParent.ControlAdded += new ControlEventHandler(this.Parent_ControlAdded);
                foreach (Control siblingControl in controlParent.Controls)
                {
                    if (siblingControl.Equals(this) == false)
                    {
                        siblingControl.LocationChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.ClientSizeChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.SizeChanged += new EventHandler(this.SiblingControls_BoundsChanged);
                    }
                }
            }
            else
            {
                controlParent.ControlAdded -= new ControlEventHandler(this.Parent_ControlAdded);
                foreach (Control siblingControl in controlParent.Controls)
                {
                    if (siblingControl.Equals(this) == false)
                    {
                        siblingControl.LocationChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.ClientSizeChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                        siblingControl.SizeChanged -= new EventHandler(this.SiblingControls_BoundsChanged);
                    }
                }
            }
        }
عملگر های ذاتی زبان که در کلاس ها تعریف شده اند به کنار، چون در لیست نمیان ولی بقیه عملا یک متد هستد که میشه فراخوانی شون کرد.
یعنی عملگر هایی که گرانبار شدن بصورت method فرضا با نامی مثل op_Multiply قابل دسترسی هستند، ولی مواردی مثل =- و =+ جزو اون سری نیستند.
بجای کار با عملگرهایی که بهشون دسترسی ندارید می توانید با System.Reflection و فراخوانی "AddEventHandler" یا "RemoveEventHandler" یعنی بصورت متدی که با نام string اش انتخاب میشه اینکاری که میخواهید رو انجام بدید، ولی چون به کاهش حجم کد یا سرعت اجرا یا حتی خوانایی کدتون کمکی نمی کنه، توصیه نمی کنم همچین کاری انجام بدید.
شما دو تا کار کاملا برعکس رو در یک متد جا کرده اید، مثل اینه که ListBox میومد Add و Remove اش رو یکی می کرد، ترکیبی که کردید همچی مناسب و سودمند نیست. دو تا متد متفاوت بهتر از اون پارامتر true / false ئه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کاری که انجام میشه و خود NET. هم استفاده کرده، تعریف یک مورد جدید با همون نام ئه (کلمه کلیدی new لازم داره). مثلا برای PictureBox مشخصه TabStop معنی نداره، چون از اون کنترل هایی است که نه میتونه Focus بگیره و نه مثل Label ها Focus رو به کنترل بعدی هدایت کنه، به همین دلیل در کد کلاس PictureBox مشخصه Control.TabStop رو از دید خارج می کنند تا PictureBox.TabStop ای به چشم نیاد. فرقی نمی کنه که اون مورد که مخفی می کنید override پذیر بوده یا نه. توجه داشته باشید که این کار برای جلوگیری از نمایش در محیط ویژوال استدیو است که Browsable و EditorBrowsable رو به رسمیت میشناسه، وگرنه قابلیت های System.Reflection برای دسترسی فراتر از اینجور پنهان سازی ها است.
کد:
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new bool TabStop
        {
            get
            {
                return base.TabStop;
            }
            set
            {
                base.TabStop = value;
            }
        }
و طبعا شما می توانید برای مورد new خودتون سطح دسترسی جدیدی در نظر بگیرید، اما اگر با اون سطح دسترسی فرضا private از خارج کلاس پنهان اش کنید دیگه نمیتونه موردی که در والدش public ئه رو پشت خودش پنهان کنه. برای همینه که TabStop ئه public تعریف شده.

سلامی مجدد استاد علی . خیلی خوشحال شدم اینجا دیدم تون :rose:
خیلی ممنون
من همچین کدی را در کلاس TransparentControl نوشتم :


کد:
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        private new string Text
        {
            get;
            set;
        }

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


کد:
        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.transparentControl.MouseUp += new MouseEventHandler(this.BtnBase_MouseUp);

            this.Controls.Add(this.transparentControl);
        }
 
آخرین ویرایش:

the_king

مدیرکل انجمن
سلامی مجدد استاد علی . خیلی خوشحال شدم اینجا دیدم تون :rose:
خیلی ممنون
من همچین کدی را در کلاس TransparentControl نوشتم :


کد:
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        private new string Text
        {
            get;
            set;
        }

اما موقعی که در فرم ام از transparentControl ، شی ایجاد کردم ، براحتی تونستم عضو Text اش (در کلاس Control) را فراخونی کنم با اونکه این عضو را private گرفته بودم (نوع شی هم از نوع TransparentControl هه) :
این بخش از پست قبلی من رو مجددا بخوانید :
و طبعا شما می توانید برای مورد new خودتون سطح دسترسی جدیدی در نظر بگیرید، اما اگر با اون سطح دسترسی فرضا private از خارج کلاس پنهان اش کنید دیگه نمیتونه موردی که در والدش public ئه رو پشت خودش پنهان کنه. برای همینه که TabStop ئه public تعریف شده.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
این بخش از پست قبلی من رو مجددا بخوانید :

خیلی ممنون استاد علی
پس بالاخره شدنی نیست . درسته؟
چه public بگیریم ، باز در لیست میاد . private هم بگیریم (در صورتی که از TransparentControl شی بسازیم) ، باز عضو Text از کلاس والدش یعنی Control لیست میشه.
 

the_king

مدیرکل انجمن
خیلی ممنون استاد علی
پس بالاخره شدنی نیست . درسته؟
چه public بگیریم ، باز در لیست میاد . private هم بگیریم (در صورتی که از TransparentControl شی بسازیم) ، باز عضو Text از کلاس والدش یعنی Control لیست میشه.
ایرادی که به اون مثالی که زدم می گیرید چیه؟ شما فرضا PictureBox.TabStop رو در کدوم لیست می بینید؟
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ایرادی که به اون مثالی که زدم می گیرید چیه؟ شما فرضا PictureBox.TabStop رو در کدوم لیست می بینید؟

پس حتما من دقیق متوجه نشدم منظورتون چیه . من عضو Text را public هم گرفتم ، باز هم توی لیست بود (که بدیهی هه)
پس لطفا کد دقیقی که باعث بشه عضو Text دیگه در لیست اینتل لایسنس وقتی که از TransparentControl شی درست میکنم ، نیاد را میذارین؟
چون من از گفته ی شما ، همون کد را برداشت کردم که انگار میگین درست نیست
خیلی ممنون
 

the_king

مدیرکل انجمن
پس حتما من دقیق متوجه نشدم منظورتون چیه . من عضو Text را public هم گرفتم ، باز هم توی لیست بود (که بدیهی هه)
پس لطفا کد دقیقی که باعث بشه عضو Text دیگه در لیست اینتل لایسنس وقتی که از TransparentControl شی درست میکنم ، نیاد را میذارین؟
چون من از گفته ی شما ، همون کد را برداشت کردم که انگار میگین درست نیست
خیلی ممنون
باید public باشه.
لابد شما در کد خودتون دارید تست می کنید. EditorBrowsable مواردی رو که داخل پروژه خودتون هستند رو مخفی نمی کنه. به این طریق تست اش کنید که در یک پروژه Library قرارش بدهید و Build کنید و در پروژه دیگری به اون dll که ساخته اید رفرنس بدهید و از کنترل تون استفاده کنید.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
خیلی ممنون استاد علی
اگه به TransparentControl ای که نوشتم ، دقت کنید ، شامل 3 کلاس هست . یکی خود TransparentControl هست . یکی شی TransparentControlBitmap که شامل اطلاعات اشیاء بیت مپ ، برای رسم هست . یکی هم شی TransparentControlText که شامل اطلاعات اشیاء متن ، برای رسم هست . کلاس TransparentControl ، از اطلاعات کلاس TransparentControlBitmap برای رسم بیت مپ و از اطلاعات کلاس TransparentControlText برای رسم متن مورد نظر ، استفاده میکنه .

حالا قضیه اینه که درون متد سازنده ی کلاس TransparentControlText ، نیاز دارم که به اطلاعات شی TransparentControl ، دسترسی داشته باشم (چون گاها نیاز دارم مثلا اندازه ی کنترل TransparentControl رو بدونم برای تنظیم کردنِ اندازه ی شی TransparentControl با اندازه ی TransparentControlText در پروپرتی AutoSize که این پروپرتی در کلاس TransparentControl هست و همچنین موارد دیگه که خیلی لازمم میشه که از درون کلاس TransparentControlText ، به شی کلاس TransparentControl ، دسترسی داشته باشم . این را هم در نظر بگیرید که چندین متد سازنده برای کلاس TransparentControlText داریم که هر کدوم شون از یک یا چند پروپرتی مختلفِ کلاسِ TransparentControl به عنوان اطلاعاتی که لازم دارن ، استفاده میکنند) . از طرفی ، داخل متد سازنده ی TransparentControl ، برنامه نویس باید شی ای از کلاس TransparentControlText بده (تا اون کلاس ، اطلاعات متنی که قراره رسم کنه که شامل مکان ، باند ، براش ، فونت ، سایز ، راست چین و چپ چین بودنش و ... هست را بدونه) . اما درونِ متد سازنده ی TransparentControlText ، شی از TransparentControl را نمیذارم (با اونکه لازمش دارم . دلیلش را همونطور که میدونید ، در ادامه میگم).

پس همونطور که مشخص هست ، قبل از اینکه ظرف شی ای از TransparentControl بسازه ، چون داخل متد سازنده ی همین TransparentControl ، شی ای از TransparentControlText میخواد ، پس باید قبل اش ، شی ای از TransparentControlText بسازه . از طرفی هم همونطور که گفتم ، در متد سازنده ی TransparentControlText ، احتیاج داریم که به شی TransparentControl دسترسی داشته باشیم. دلیل اینکه درونِ متد سازنده ی TransparentControlText ، شی از TransparentControl را نمیذارم هم همینه که چون ابتدا ، شی TransparentControlText ساخته میشه و بعدش شی TransparentControl ، پس زمان اجرا شدن متد سازنده ی TransparentControlText ، شی TransparentControl ای ساخته نشده و null هست . پس نمیشه در متد سازنده ی TransparentControlText ، شی ای از TransparentControl را دریافت کنم . اگه میشد هم ، ناجور میشد و نمیخوام برنامه نویس ، این شی TransparentControl را در متد سازنده ی TransparentControlText بده . چون شی ها از هم جدا هستن و به کاربر و برنامه نویسی که از این کنترلم استفاده میکنه ، مربوط نمیشه که شی TransparentControl را درون متد سازنده ی TransparentControlText بده.




حالا به نظرتون من چی کار میتونم کنم که مشکل بالا را با ساده ترین کدنویسی ، حل کنم؟
چند راه که به نظرم میرسه اینا هستن اما یا کارایی ندارن ، یا کدنویسی را یه کم پیچیده میکنن یا به نظرم میاد که اصولی نباشن :

1) اینکه کلا بی خیال کلاس TransparentControlText بشم و کدهاش را در کلاس TransparentControl ادغام کنم . این ، ساده ترین و بی دردسرترین راه حل هست اما چون میخوام بخشِ متن ، یه شی و یه کلاس جدا باشه ، حتی الامکان این کار را نمیخوام انجام بدم.

2) مثل کلاس TransparentControlBitmap ، یه پروپرتی (با سطح دسترسی public) از نوع TransparentControl درون کلاس TransparentControlText تعریف کنم (همونطور که گفتم ، نمیتونم نوع TransparentControl را درون متد سازنده ی TransparentControlText بگیرم چون قبل اش ، شی ای از TransparentControl ساخته نشده و هم اینکه معنا نداره که برنامه نویس این مقدار را بده) . درون متد سازنده ی TransparentControl ، به این پروپرتی (TransparentControl که درون کلاس TransparentControlText تعریف کردم) مقدار بدم . درون قسمت set در این پروپرتی ، (چون نمیتونم دیگه متد سازنده ی کلاس TransparentControlText را دیگه فراخونی کنم (چون شی جدیدِ دیگه ای ساخته میشه) اون بخش از کدی که داخل متد سازنده ی کلاس TransparentControlText ای که نیاز به اطلاعاتِ کلاس TransparentControl داشت را اول به یه تابعِ دیگه ای منتقل کنم و بعد درون قسمت set در این پروپرتی از نوع TransparentControl درون کلاس TransparentControlText) ، این متد را فراخونی کنم .
اما یه مشکلی که هنوز پابرجا میمونه اینه که چون در هر کدوم از متدهای سازنده ی کلاس TransparentControlText ، نیاز دارم به یکی از اطلاعات (منظورم همون یکی از اعضا و پروپرتی های) کلاس TransparentControl ، دسترسی داشته باشم و فقط همون پروپرتی را فراخونی کنم ، دیگه درون قسمت Set در این پروپرتی (از نوع TransparentControl درون کلاس TransparentControlText) ؛ دیگه نمیدونم برنامه نویس ، کدومیک از متد سازنده ی کلاس TransparentControlText را فراخونی کرده . پس برای حل این مشکل ، باید یه فلگ بذارم و درون متد سازنده های مختلف (در کلاس TransparentControlText) مقداردهی شون کنم و درون اون متدی که در قسمت Set پروپرتی (از نوع TransparentControl درون کلاس TransparentControlText) فراخونی کردم ، برای اون فلگ ، شرط بذارم (که اگه برابر با فلان مقدار بود ، اطلاعاتی از کلاس TransparentControl را که درون فلان متد سازنده لازم داشتی را فراخونی کن و کدهای مربوط به اون قسمت را بنویس) .

اما فکر میکنم روش دوم ، یه کم کار را پیچیده میکنه و اصولی نیست .
کلا مناسب ترین و بهترین روش برای این کار، چیه؟
خیلی ممنون :rose:
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد علی
اگه به TransparentControl ای که نوشتم ، دقت کنید ، شامل 3 کلاس هست . یکی خود TransparentControl هست . یکی شی TransparentControlBitmap که شامل اطلاعات اشیاء بیت مپ ، برای رسم هست . یکی هم شی TransparentControlText که شامل اطلاعات اشیاء متن ، برای رسم هست . کلاس TransparentControl ، از اطلاعات کلاس TransparentControlBitmap برای رسم بیت مپ و از اطلاعات کلاس TransparentControlText برای رسم متن مورد نظر ، استفاده میکنه .

حالا قضیه اینه که درون متد سازنده ی کلاس TransparentControlText ، نیاز دارم که به اطلاعات شی TransparentControl ، دسترسی داشته باشم (چون گاها نیاز دارم مثلا اندازه ی کنترل TransparentControl رو بدونم برای تنظیم کردنِ اندازه ی شی TransparentControl با اندازه ی TransparentControlText در پروپرتی AutoSize که این پروپرتی در کلاس TransparentControl هست و همچنین موارد دیگه که خیلی لازمم میشه که از درون کلاس TransparentControlText ، به شی کلاس TransparentControl ، دسترسی داشته باشم . این را هم در نظر بگیرید که چندین متد سازنده برای کلاس TransparentControlText داریم که هر کدوم شون از یک یا چند پروپرتی مختلفِ کلاسِ TransparentControl به عنوان اطلاعاتی که لازم دارن ، استفاده میکنند) . از طرفی ، داخل متد سازنده ی TransparentControl ، برنامه نویس باید شی ای از کلاس TransparentControlText بده (تا اون کلاس ، اطلاعات متنی که قراره رسم کنه که شامل مکان ، باند ، براش ، فونت ، سایز ، راست چین و چپ چین بودنش و ... هست را بدونه) . اما درونِ متد سازنده ی TransparentControlText ، شی از TransparentControl را نمیذارم (با اونکه لازمش دارم . دلیلش را همونطور که میدونید ، در ادامه میگم).

پس همونطور که مشخص هست ، قبل از اینکه ظرف شی ای از TransparentControl بسازه ، چون داخل متد سازنده ی همین TransparentControl ، شی ای از TransparentControlText میخواد ، پس باید قبل اش ، شی ای از TransparentControlText بسازه . از طرفی هم همونطور که گفتم ، در متد سازنده ی TransparentControlText ، احتیاج داریم که به شی TransparentControl دسترسی داشته باشیم. دلیل اینکه درونِ متد سازنده ی TransparentControlText ، شی از TransparentControl را نمیذارم هم همینه که چون ابتدا ، شی TransparentControlText ساخته میشه و بعدش شی TransparentControl ، پس زمان اجرا شدن متد سازنده ی TransparentControlText ، شی TransparentControl ای ساخته نشده و null هست . پس نمیشه در متد سازنده ی TransparentControlText ، شی ای از TransparentControl را دریافت کنم . اگه میشد هم ، ناجور میشد و نمیخوام برنامه نویس ، این شی TransparentControl را در متد سازنده ی TransparentControlText بده . چون شی ها از هم جدا هستن و به کاربر و برنامه نویسی که از این کنترلم استفاده میکنه ، مربوط نمیشه که شی TransparentControl را درون متد سازنده ی TransparentControlText بده.




حالا به نظرتون من چی کار میتونم کنم که مشکل بالا را با ساده ترین کدنویسی ، حل کنم؟
چند راه که به نظرم میرسه اینا هستن اما یا کارایی ندارن ، یا کدنویسی را یه کم پیچیده میکنن یا به نظرم میاد که اصولی نباشن :

1) اینکه کلا بی خیال کلاس TransparentControlText بشم و کدهاش را در کلاس TransparentControl ادغام کنم . این ، ساده ترین و بی دردسرترین راه حل هست اما چون میخوام بخشِ متن ، یه شی و یه کلاس جدا باشه ، حتی الامکان این کار را نمیخوام انجام بدم.

2) مثل کلاس TransparentControlBitmap ، یه پروپرتی (با سطح دسترسی public) از نوع TransparentControl درون کلاس TransparentControlText تعریف کنم (همونطور که گفتم ، نمیتونم نوع TransparentControl را درون متد سازنده ی TransparentControlText بگیرم چون قبل اش ، شی ای از TransparentControl ساخته نشده و هم اینکه معنا نداره که برنامه نویس این مقدار را بده) . درون متد سازنده ی TransparentControl ، به این پروپرتی (TransparentControl که درون کلاس TransparentControlText تعریف کردم) مقدار بدم . درون قسمت set در این پروپرتی ، (چون نمیتونم دیگه متد سازنده ی کلاس TransparentControlText را دیگه فراخونی کنم (چون شی جدیدِ دیگه ای ساخته میشه) اون بخش از کدی که داخل متد سازنده ی کلاس TransparentControlText ای که نیاز به اطلاعاتِ کلاس TransparentControl داشت را اول به یه تابعِ دیگه ای منتقل کنم و بعد درون قسمت set در این پروپرتی از نوع TransparentControl درون کلاس TransparentControlText) ، این متد را فراخونی کنم .
اما یه مشکلی که هنوز پابرجا میمونه اینه که چون در هر کدوم از متدهای سازنده ی کلاس TransparentControlText ، نیاز دارم به یکی از اطلاعات (منظورم همون یکی از اعضا و پروپرتی های) کلاس TransparentControl ، دسترسی داشته باشم و فقط همون پروپرتی را فراخونی کنم ، دیگه درون قسمت Set در این پروپرتی (از نوع TransparentControl درون کلاس TransparentControlText) ؛ دیگه نمیدونم برنامه نویس ، کدومیک از متد سازنده ی کلاس TransparentControlText را فراخونی کرده . پس برای حل این مشکل ، باید یه فلگ بذارم و درون متد سازنده های مختلف (در کلاس TransparentControlText) مقداردهی شون کنم و درون اون متدی که در قسمت Set پروپرتی (از نوع TransparentControl درون کلاس TransparentControlText) فراخونی کردم ، برای اون فلگ ، شرط بذارم (که اگه برابر با فلان مقدار بود ، اطلاعاتی از کلاس TransparentControl را که درون فلان متد سازنده لازم داشتی را فراخونی کن و کدهای مربوط به اون قسمت را بنویس) .

اما فکر میکنم روش دوم ، یه کم کار را پیچیده میکنه و اصولی نیست .
کلا مناسب ترین و بهترین روش برای این کار، چیه؟
خیلی ممنون :rose:
شما دو گزینه دارید، یا باید برای کنترل تون مشخصه هایی داشته باشید که بر اساس اونها ظاهر کنترل رو تعیین کنه، مثل Button.TextAlign و Button.ForeColor و Button.Font و Button.Image و ...
یا مشخصه های کنترل رو کنار بذارید و بجایش رخداد هایی داشته باشید که برنامه نویس بتونه توسط اونها ظاهر کنترل رو خودش رسم کنه، مثل ListBox.DrawItem که هر آنچه برنامه نویس لازم داره در DrawItemEventArgs همون رخداد در دسترس قرار داده. گزینه اول برای سادگی کار برنامه نویسه که با یکسری مشخصه های محیط ویژوال بدون دردسر ظاهر رو تعیین کنه و گزینه دومی برای حداکثر انعطاف پذیری در رسم.
کاری که شما در کدتون کردید نه کار شما رو راحت تر می کنه و نه کار برنامه نویسی که میخواد از کنترل تون استفاده کنه. تا جایی که میشده کار رو سخت کردید.
شما دارید مجبورش می کنید برای تعیین مورد ساده ای مثل موقعیت Text بجای مشخصه در دسترس Button.TextAlign مجبور بشه با شی سازی از کلاس دیگری مثل TransparentControlText و مشخصه های مجزای TransparentControlText.StringFormat.Alignment و TransparentControlText.StringFormat.LineAlignment درگیر بشه. شما هیچ کنترلی پیدا نمی کنید که شما رو وادار کنه برای تعیین مشخصات متن StringFormat بسازید، جز کنترل خودتون. مشخصه هایی مثل TextBound هم باید بر اساس طول و عرض متن (Graphics.MeasureString) و TextBrush بر اساس ForeColor تعیین بشن، نه اینکه برنامه نویس مجبور بشه ابعاد متن رو بجای کنترل شما محاسبه کنه. شما دارید بهش کمک می کنید که گزینه های بیشتری داشته باشه یا میخواهید شکنجه اش کنید؟
از طرف دیگه مدام مجبور هستید اون مشخصه هایی که برای کلاس های جانبی ساختید رو تغییر بدید تا با کنترل تون هماهنگ بمونه.
اگر صرفا برای استفاده داخل کلاس میساختیدشون و private و protected بودند مانعی نبود چون شما به هر روشی که داخل کلاس کار کنید برای برنامه نویس از بیرون کلاس که اونها رو نمی بینه اهمیتی نداره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
شما دو گزینه دارید، یا باید برای کنترل تون مشخصه هایی داشته باشید که بر اساس اونها ظاهر کنترل رو تعیین کنه، مثل Button.TextAlign و Button.ForeColor و Button.Font و Button.Image و ...
یا مشخصه های کنترل رو کنار بذارید و بجایش رخداد هایی داشته باشید که برنامه نویس بتونه توسط اونها ظاهر کنترل رو خودش رسم کنه، مثل ListBox.DrawItem که هر آنچه برنامه نویس لازم داره در DrawItemEventArgs همون رخداد در دسترس قرار داده. گزینه اول برای سادگی کار برنامه نویسه که با یکسری مشخصه های محیط ویژوال بدون دردسر ظاهر رو تعیین کنه و گزینه دومی برای حداکثر انعطاف پذیری در رسم.
کاری که شما در کدتون کردید نه کار شما رو راحت تر می کنه و نه کار برنامه نویسی که میخواد از کنترل تون استفاده کنه. تا جایی که میشده کار رو سخت کردید.
شما دارید مجبورش می کنید برای تعیین مورد ساده ای مثل موقعیت Text بجای مشخصه در دسترس Button.TextAlign مجبور بشه با شی سازی از کلاس دیگری مثل TransparentControlText و مشخصه های مجزای TransparentControlText.StringFormat.Alignment و TransparentControlText.StringFormat.LineAlignment درگیر بشه. شما هیچ کنترلی پیدا نمی کنید که شما رو وادار کنه برای تعیین مشخصات متن StringFormat بسازید، جز کنترل خودتون. مشخصه هایی مثل TextBound هم باید بر اساس طول و عرض متن (Graphics.MeasureString) و TextBrush بر اساس ForeColor تعیین بشن، نه اینکه برنامه نویس مجبور بشه ابعاد متن رو بجای کنترل شما محاسبه کنه. شما دارید بهش کمک می کنید که گزینه های بیشتری داشته باشه یا میخواهید شکنجه اش کنید؟
از طرف دیگه مدام مجبور هستید اون مشخصه هایی که برای کلاس های جانبی ساختید رو تغییر بدید تا با کنترل تون هماهنگ بمونه.
اگر صرفا برای استفاده داخل کلاس میساختیدشون و private و protected بودند مانعی نبود چون شما به هر روشی که داخل کلاس کار کنید برای برنامه نویس از بیرون کلاس که اونها رو نمی بینه اهمیتی نداره.

سلامی مجدد
خیلی ممنون استاد علی.
این قدر خوشم میاد از نحوه ی صحبت تون . مثل همین کلمه ی شکنجه :green:
نه . اتفاقا برای اینکه کار را آسون کنم و رفتار این کنترل را چیزی شبیه به کنترل Label کنم ، میخوام این کار را کنم .
بذارید یه نمونه مثال بزنم. در اولین متد سازنده در کلاس TransparentControlText ، از کاربر ، فقط یک string به عنوان متن برای رسم میگیرم. اما از اونجایی که location این متن باید برای رسم مشخص بشه ، x اش را صفر اما y اش را میخوام این طور دربیارم که وسطِ (از لحاظ ارتفاع) شی TransparentControlText ام ، در وسطِ شی TransparentControl ام بیفته . اندازه ی شی TransparentControlText ام را برنامه نویس نمیتونه تغییر بده و همیشه برابر با اندازه ی فونت ای هست که برنامه نویس فونت اش را میده . فونت پیش فرض اش هم ، برابر با فونت والدش هست. پس بنابراین ، نیاز دارم که درون اولین متد سازنده اش ، به اندازه ی کنترل TransparentControl دسترسی داشته باشم.

اما منظورتون از قضیه ی رویداد را دقیق متوجه نشدم (ربطش به این قضیه رو).
کلا دقیق تر توضیح میدین که به نظرتون باید چی کار کنم؟ اگه با مثال در مقیاس خیلی کوچیک شده ، مثال بزنین که خیلی خوب میشه.
خیلی ممنون
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد علی.
این قدر خوشم میاد از نحوه ی صحبت تون . مثل همین کلمه ی شکنجه :green:
نه . اتفاقا برای اینکه کار را آسون کنم و رفتار این کنترل را چیزی شبیه به کنترل Label کنم ، میخوام این کار را کنم .
بذارید یه نمونه مثال بزنم. در اولین متد سازنده در کلاس TransparentControlText ، از کاربر ، فقط یک string به عنوان متن برای رسم میگیرم. اما از اونجایی که location این متن باید برای رسم مشخص بشه ، x اش را صفر اما y اش را میخوام این طور دربیارم که وسطِ (از لحاظ ارتفاع) شی TransparentControlText ام ، در وسطِ شی TransparentControl ام بیفته . اندازه ی شی TransparentControlText ام را برنامه نویس نمیتونه تغییر بده و همیشه برابر با اندازه ی فونت ای هست که برنامه نویس فونت اش را میده . فونت پیش فرض اش هم ، برابر با فونت والدش هست. پس بنابراین ، نیاز دارم که درون اولین متد سازنده اش ، به اندازه ی کنترل TransparentControl دسترسی داشته باشم.
شما اصلا به TransparentControlText نیازی نداشتید که حالا ربطش بدید به TransparentControl. تازه اگر TransparentControlText باید از مشخصه های TransparentControl آگاه باشه میتوانید یک فیلد private/protected رو اختصاص بدید برای ارجاع به TransparentControl. مثل خیلی از کلاس های NET. که با فیلد غیر عمومی به منبعی دسترسی دارند.

وقتی میخواهید مثل Label باشه مثل خود Label کد نویسی کنید، چرا از چیزی که هست الگو نمی گیرید. مشخصه های Label.TextAlign و Label.Text و Label.ForeColor و Label.Font و Label.RightToLeft و Label.Padding همگی دست به دست هم میدن تا موقع رسم Rectangle و StringFormat و Brush ای ایجاد بشه و رسم متن انجام بگیره. Padding روی موقعیت رسم تاثیر میذاره، خیلی ساده و بی دردسر.
Label که نیومده برای Rectangle و StringFormat و Brush اش کلاس public تعریف کنه و به شما بگه مشخصه های این کلاس رو تعیین کن. برنامه نویس با مشخصه های TextAlign و Text و ForeColor و ... کار می کنه، نه پارامتر هایی مثل Rectangle و StringFormat و Brush که موقع رسم کنترل ایجاد میشن. در ضمن Brush ای که خودتون بسازید و جزو SystemBrushes نباشه، باید حتما بعد از استفاده Dispose و آزاد بشه، مثل هر کلاس دیگری که IDisposable رو پیاده سازی کرده و متد Dispose داره. جزو منابع سیستم ئه. موردی نیست که بخواهید در مشخصه ای برای مدت طولانی نگهش دارید. شرایط اش با Color که منبع نیست فرق داره.

اما منظورتون از قضیه ی رویداد را دقیق متوجه نشدم (ربطش به این قضیه رو).
رسم خودش یک رویداد ئه، ولی کافی نیست. فرض کنید کلاسی شبیه ListBox میسازید و میخواهید شیوه رسم ListBox رو برنامه نویس بتونه تغییر بده. ببینید چیزی بهتر از اون کاری که ListBox کرده به ذهنتون میرسه؟
حتی اگه ListBox صد تا مشخصه اضافی هم میداشت برای انعطاف پذیری در رسم چیزی که دلخواه شما است کافی نبود. شما ممکن بود بخواهید در ListBox جدول رنگ نشون بدید، تایمر نشون بدید، آیکون نشون بدید و ...
برای اینها مشخصه کافی نیست، باید رخداد رسم رو دست برنامه نویس بسپارید، اگر کل رخداد Paint رو به برنامه نویس واگذار کنید که برایش مصیبت ئه، چون باید درگیر پارامتر های خیلی زیادی بشه.
اما ListBox.DrawItem برای همینه که بخش زیادی از سختی رسم رو از بین میبره. اولا تمامی پارامتر هایی که مربوط به یک آیتم که داره رسم میشه در این رخداد داخل DrawItemEventArgs هست. مثل کلاس شما نیست که برنامه نویس تعیین اش کنه، موقع بروز رخداد DrawItem مقادیرش پر شده به برنامه نویس میرسه. ثانیا برنامه نویس به متد هایی مثل DrawItemEventArgs.DrawBackground و DrawItemEventArgs.DrawFocusRectangle دسترسی داره و میتونه صرفا روی رسم خود آیتم متمرکز بشه، نه زمینه و Focus. ثالثا DrawItemEventArgs.State برنامه نویس رو از وضعیت آیتم مطلع می کنه، حالا برنامه نویس هم میتونه این وضعیت رو در نظر بگیره و هم میتونه نگیره. از طرفی دست برنامه نویس برای شیوه رسم خیلی باز شده و از طرف دیگه رسم برایش سخت نیست چون همه اجزاء لازم برایش مهیا است.
 

جدیدترین ارسال ها

بالا