سلام
من مدتیه که با ویژوال سی شارپ برنامه می نویسم ، و چندین منبع و 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 می شود.
کد کامل پروژه ضمیمه این پست می باشد.