بهطور پیشفرض، سیشارپ کنترلها و فرم را بهصورت مستقیم روی صفحه رسم میکند که منجر به Flickering و سیاهی لحظهای میشود. Double Buffering تصویر فرم را ابتدا در یک حافظه پنهان (Off-screen buffer) رسم کرده و پس از اتمام پردازش گرافیکی، کل تصویر را یکباره روی مانیتور قرار میدهد.
الف) فعالسازی روی خود فرم
در سازنده فرم (Constructor)، خطوط زیر را اضافه کنید:
public MainForm()
{
InitializeComponent();
// فعالسازی Double Buffering برای فرم
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();
}
ب) اعمال Double Buffering روی تمامی کنترلهای درون فرم (Panel, TableLayoutPanel, DataGridView)
خاصیت DoubleBuffered در برخی کنترلها مانند Panel یا TableLayoutPanel در سطح protected قرار دارد و از طریق Property Grid در ویژوال استودیو قابل دسترسی نیست. برای رفع این مشکل، میتوانید از یک کلاس Helper با Reflection برای تمامی کنترلهای فرم استفاده کنید:
public static class ExtensionMethods
{
public static void EnableDoubleBuffer(this Control control)
{
var propertyInfo = typeof(Control).GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
propertyInfo?.SetValue(control, true, null);
}
}
// نحوه استفاده در متد Form_Load یا Constructor:
private void MainForm_Load(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
c.EnableDoubleBuffer();
}
}
هنگام لود فرم یا اضافه شدن کنترلهای متعدد، سیستمعامل Windows با هر تغییر اندازه یا درج کنترل جدید، عملیات Layout و Paint را مجدداً اجرا میکند. با متوقف کردن این پردازشها تا پایان بارگذاری کامل، لود فرم بسیار سریعتر و بدون پرش انجام میشود.
private void LoadControlsDynamically()
{
// ۱. متوقف کردن چیدمان و رسم گرافیکی
this.SuspendLayout();
try
{
// اضافه کردن یا تغییر سایز کنترلها
for (int i = 0; i < 50; i++)
{
Button btn = new Button { Text = $"دکمه {i}", Location = new Point(10, i * 30) };
this.Controls.Add(btn);
}
}
finally
{
// ۲. بازگرداندن وضعیت به حالت عادی و اجرای یکباره Layout
this.ResumeLayout(true);
}
}
یکی از موثرترین روشها برای جابهجایی و لود فرمهای سنگین بدون سیاهی و پرش، دستکاری پارامترهای ساخت فرم در سطح API ویندوز است. با افزودن پرچم WS_EX_COMPOSITED (به مقدار 0x02000000) به فرم، سیستمعامل مجبور میشود تمام کنترلهای فرزند فرم را بهصورت یک لایه یکپارچه در حافظه پنهان رندر کند.
کد زیر را درون کلاس فرم خود قرار دهید:
protected override CreateParams CreateParams
{
get;
{
CreateParams cp = base.CreateParams;
// WS_EX_COMPOSITED: رندر یکپارچه تمام فرزندان فرم در حافظه
cp.ExStyle |= 0x02000000;
return cp;
}
}
نکته: این روش سیاهی و Flickering ناشی از لود کنترلها را کاملاً از بین میبرد، اما ممکن است در سیستمهای قدیمی مصرف حافظه رم را کمی افزایش دهد.
شایعترین دلیل سیاهی فرم هنگام نمایش، اجرای کدهای زمانبر در متد Form_Load یا سازنده فرم است. اجرای کدهایی نظیر کوئریهای EF Core/SQL Server، خواندن فایلها، یا فراخوانی REST API بهصورت synchronous، نخ UI را مسدود کرده و مانیتور قادر به دریافت پیامهای WM_PAINT نخواهد بود.
روش اشتباه (Synchronous):
// این کد باعث سیاهی و هنگ کردن لحظهای فرم میشود
private void MainForm_Load(object sender, EventArgs e)
{
var data = repository.GetHeavyDataFromDatabase(); // مسدود شدن UI
dataGridView1.DataSource = data;
}
روش صحیح (Asynchronous):
private async void MainForm_Load(object sender, EventArgs e)
{
// فرم نمایش داده میشود اما لیست در حال بارگذاری است
showLoadingSpinner(true);
// اجرا در پسزمینه بدون مسدود کردن UI Thread
var data = await Task.Run(() => repository.GetHeavyDataFromDatabaseAsync());
dataGridView1.DataSource = data;
showLoadingSpinner(false);
}
رویداد Form_Load قبل از رندر شدن فرم و روی صفحه قرار گرفتن آن اجرا میشود. اگر کدهای سنگین را به رویداد Form_Shown منتقل کنید، فرم ابتدا بهصورت کاملاً رندرشده به کاربر نمایش داده میشود و سپس عملیات بارگذاری دیتابیس یا فایلها آغاز خواهد شد.
|
رویداد |
زمان اجرا |
کاربرد مناسب |
|
Constructor |
هنگام ساخت Instance کلاس |
مقداردهی اولیه متغیرها و InitializeComponent |
|
Form_Load |
قبل از ظاهر شدن فرم |
تنظیمات ظاهری سبک (Theme، فونت، دسترسیها) |
|
Form_Shown |
پس از رسم کامل فرم روی مانیتور |
کوئریهای دیتابیس، فراخوانی API و لود دادهها |
private async void MainForm_Shown(object sender, EventArgs e)
{
// فرم با سرعت بالا ظاهر شده و کاربر سیاهی نمیبیند
await LoadDashboardDataAsync();
}
کاهش تعداد کنترلهای همزمان: استفاده از تعداد بسیار زیاد کنترل (مانند ۵۰ عدد Label یا Panel جداگانه) فشار مضاعفی به GDI+ وارد میکند. در صورت امکان، دادهها را در کنترلهای مجتمع مانند DataGridView یا ListView با حالت VirtualMode نمایش دهید.
استفاده از BeginUpdate و EndUpdate: در کنترلهایی نظیر ListBox ،ListView ،TreeView و ComboBox قبل از افزودن انبوه دادهها، رسم را متوقف کنید:
treeView1.BeginUpdate();
// افزودن ۱۰۰۰ نود به درخت
treeView1.Nodes.AddRange(...);
treeView1.EndUpdate();
جایگزینی TableLayoutPanel: کنترل TableLayoutPanel در WinForms به دلیل محاسبات پیچیده چیدمان (Layout Engine)، کندترین کنترل در رندر شدن است. در صورت بروز تاخیر، آن را با Panel ساده یا چیدمان دستی/کدنویسیشده جایگزین کنید.
سختافزار و Hardware Acceleration: فرمهای WinForms بهصورت متکی بر پردازنده (CPU) و API قدیمی GDI+ رندر میشوند. برای فرمهای فوقالعاده سنگین گرافیکی، استفاده از UserControlهای بهینهشده یا مهاجرت بخشهای گرافیکی به WPF / SkiaSharp پیشنهاد میشود.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.