چند مشکل در visual #c

ali_tavanaie

Well-Known Member
سلام
من مدتیه که با ویژوال سی شارپ برنامه می نویسم ، و چندین منبع و msdn رو زیر و رو کردم ، اما جواب مناسبی پیدا نکردم.
اما سوال های من :
1- زمانی که یک شکل گرافیکی رو رسم می کنم ، هنگامی که یک صفحه دیگه از روی برنامه من عبور می کنه ، اون مقدار از drawing من پاک میشه و باید دوباره رسمش کنم.چه کر کنم که این مشکل برطرف شه.
2-چند عدد عکس دارم که می خوام با تغییر یک فیلد دیگه در یک picturebox لود بشن ، به resource ها هم اضافه کردم ، اما باز هم هنگامی که می خوام به picturebox نسبت بدم ارور می ده ، میخواستم بدونم روال اصلی این کار چه جوریه.
سوال باز هم دارم ، اما تا اینجا ببینیم چی پیش میاد.
 

the_king

مدیرکل انجمن
سلام
من مدتیه که با ویژوال سی شارپ برنامه می نویسم ، و چندین منبع و msdn رو زیر و رو کردم ، اما جواب مناسبی پیدا نکردم.
اما سوال های من :
1- زمانی که یک شکل گرافیکی رو رسم می کنم ، هنگامی که یک صفحه دیگه از روی برنامه من عبور می کنه ، اون مقدار از drawing من پاک میشه و باید دوباره رسمش کنم.چه کر کنم که این مشکل برطرف شه.

لطفا هر سوال رو در یک تاپیک جداگانه مطرح کنید، اینطوری می توانید در عنوان تاپیک دقیقا موضوع سوال تون
رو بنویسید و برای سایر کاربران که ممکنه مشکلی نظیر مشکل شما رو داشته باشند پیدا کردن تاپیک مورد نظر
ساده تر میشه.

در سیستم عامل ویندوز زمانی که در حالت عادی روی یک پنجره چیزی ترسیم میشه صرفا در حافظه بافر (Buffer)
موقتی ثبت میشه که مستقیما توسط کارت گرافیکی نمایش داده میشه، این حافظه کل صفحه نمایش رو پوشش میده
و ممکنه هر لحظه توسط یک تغییر در آرایش صفحه و یا یک پنجره جدید ترسیم قبلی رو حذف کنه. این نوع نمایش
به دلیل عدم نیاز به یک حافظه Bitmap اضافی هم سرعت اجرای زیادی داره و هم حافظه کمی از سیستم
اشغال میشه.

اما همانطور که اشاره کردید هم مشکل از بین رفتن ترسیم پیش میاد و هم اگه بخواهید برای جبران این مشکل
اون ترسیم رو مجددا و بارها و بارها انجام دهید حالت پر پر زدن پنجره (Flicker) کاربر رو آزار میده.
برای رفع این مورد نیاز به یک حافظه بافر اضافی است که به اصطلاح بهش بافر مضاعف یا Double Buffer می گویند.
این حافظه اضافی هر آنچه که شما در پنجره ترسیم می کنید را ابتدا در خودش ذخیره کرده و سپس به بافر اصلی
انتقال می دهد. هر بار که پنجره جابجا میشه و نیاز به ترسیم مجدد پنجره هست، اون بافر همچنان ترسیم قبلی
رو در حافظه اش داره و به صفحه نمایش انتقال میده. چنین شیوه ای کمی کند تر است، اما هیچگاه پنجره پرپر نمی زند
و به اصطلاح Flicker Free است.

برای ایجاد کردن چنین حالتی در #C از شیوه زیر استفاده می کنیم :
یک بافر می سازیم و از PictureBox می خواهیم که هر بار خواست خودش را Paint کند، محتویات بافر را در خود
نمایش دهد. خودمان هر بار که قصد ترسیم چیزی را داشتیم بجای آنکه مستقیما در PictureBox رسم کنیم
در buffer ترسیم اش کرده و سپس PictureBox را از تغییر کردن محتویات بافر با خبر می کنیم تا نمایش دهد.
چون بافر در کلاس تعریف می شود، تا زمانی که فرم بسته نشده محتویات داخلش را حفظ می کند و چیزی
از ترسیم های ما حذف نمی شود.

ابتدای کد فرم یک buffer از کلاس BufferedGraphics برای نگهداری ترسیم ها تعریف می کنیم :
کد:
[COLOR="Gray"]    public partial class Form1 : Form
    {
[/COLOR][COLOR="Blue"][B]        private BufferedGraphics buffer;[/B][/COLOR]

[COLOR="Gray"]        public Form1()
        {
            InitializeComponent();
        }
[/COLOR]

زمانی که فرم Load می شود این بافر را ایجاد می کنیم، پس بایستی برای رخداد مربوطه کد نویسی کنیم :
کد:
        private void Form1_Load(object sender, EventArgs e)
        {
            Rectangle bounds = pictureBox1.Bounds;
            BufferedGraphicsContext bufferedContext = BufferedGraphicsManager.Current;
            buffer = bufferedContext.Allocate(pictureBox1.CreateGraphics(), bounds);
            buffer.Graphics.FillRectangle(Brushes.White, bounds);
        }
کد بالا بافر را ایجاد می کند، چون رنگ پیشفرض در بافر 0 (مشکی است) یک دستور FillRectangle به آن
اضافه کردم تا رنگ زمینه را به سفید تغییر دهد.

رخداد ترسیم شدن PictureBox هم بایستی محتویات این بافر ما را روی صفحه نمایش دهد :
کد:
        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            buffer.Render(e.Graphics);
        }

دقت کنید که از این به بعد برای ترسیم در PictureBox مستقیما از e.Graphics یا pictureBox1.CreateGraphics()
استفاده نکنید چون آنها کاری با بافر ما ندارند و ترسیم ها در حافظه buffer ذخیره نمی گردند. بجایش
برای رسم هر چیزی در PictureBox از buffer.Graphics استفاده کنید ، مثلا کد زیر یک خط با مختصات تصادفی
را در buffer ترسیم کرده و سپس PictureBox را مجبور به ترسیم مجدد بافر می کند (Invalidate)
کد:
            Random rnd = new Random();
            Point p1 = new Point(rnd.Next(pictureBox1.Width), rnd.Next(pictureBox1.Height));
            Point p2 = new Point(rnd.Next(pictureBox1.Width), rnd.Next(pictureBox1.Height));
            buffer.Graphics.DrawLine(Pens.Black, p1, p2);
            pictureBox1.Invalidate();
توجه داشته باشید که بافر بصورت مستقیم به PictureBox وصل نیست و اگر ترسیمی در آن انجام شود
PictureBox بلافاصله با خبر نمی شود. پس اگر چیزی در buffer رسم کردید بایستی برای نمایش آن
به PictureBox فرمان Invalidate را بدهید، این کار عملا موجب فراخوانی رخداد Paint می شود.

کد کامل پروژه ضمیمه این پست می باشد.
 

پیوست ها

  • FlickerFreeDrawing.zip
    36.7 کیلوبایت · بازدیدها: 2

the_king

مدیرکل انجمن
2-چند عدد عکس دارم که می خوام با تغییر یک فیلد دیگه در یک picturebox لود بشن ، به resource ها هم اضافه کردم ، اما باز هم هنگامی که می خوام به picturebox نسبت بدم ارور می ده ، میخواستم بدونم روال اصلی این کار چه جوریه.
سوال باز هم دارم ، اما تا اینجا ببینیم چی پیش میاد.

نمی دونم دقیقا از چه شیوه ای برای نمایش تصویر ها استفاده می کنید، به هر حال اگر در منوی Project گزینه
xxxx Properties (عبارت xxxx نام پروژه شما است) را انتخاب کنید در بخش Resources می توانید از دکمه
Add Resource و سپس Add Existing File استفاده کرده و فایل های مورد نظرتون رو به پروژه اضافه کنید.

اکنون با عبارت Properties.Resources در کدتان به محتویات اضافه شده دسترسی دارید و مثلا اگر تصویری
با عنوان Question.png رو اضافه کرده اید، Properties.Resources.Question تصویر مورد نظر است و
با کدی شبیه به کد زیر می توانید آنرا در PictureBox نمایش دهید :
کد:
            pictureBox1.Image = Properties.Resources.Question;

کد کامل پروژه نمونه ضمیمه این پست می باشد.
 

پیوست ها

  • LoadPictureFromResources.zip
    489.1 کیلوبایت · بازدیدها: 6

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

بالا