[C#] Automate Control.InvokeRequired Code pattern

Blog นี้จริงๆ แล้วเป็นการนำ Blog ที่เขียนค้างตอนปี 2014 (ช่วงที่อยู่กับ Project ที่ได้ แต่บ่น 555) มาเขียนต่อให้จบครับ แม้ว่าตอนนี้แนวทางการพัฒนาระบบเป็นแนว Web Application แล้ว แต่ยังมีงานบางส่วนที่ยังเป็น Desktop Application ครับ

ปัญหา

  • เวลาจัดการงานด้าน UI มักจะเจอปัญหาที่เจอประจำเลย InvalidOperationException
Cross-thread operation not valid. Control accessed from a thread other than the thread it was created on. 
  • สาเหตุ เกิดจากตัว Control ของ WinForms ไม่เป็น Thread-Safe ครับ
  • ทางแก้ไข มี 2 แบบครับ
    • BackgroundWorker: ให้ Main Thread แอบสั่งทำงานแบบ Asynchronous ผ่าน Method dowork
    • Invoke method: delegated จาก Main Thread ให้มาทำส่วนที่ต้องการแทนครับ
  • คราวนี้ เราจะมา Focus เฉพาะในส่วนของ Invoke method

Invoke method

  • การใช้งานง่ายมาครับ มี Pattern ดังนี้ครับ
if (<<CONTROL>>.InvokeRequired)
{
	<<CONTROL>>.Invoke(new Action(() =>
	{
		<<YOUR LOGIC>>
	}));
}
else
{
	<<YOUR LOGIC>>
}
  • ตัวอย่าง
if (cboPortfolio.InvokeRequired)
{
	cboPortfolio.Invoke(new Action(() =>
	{
		cboPortfolio._lookUpEdit.EditValue = o;
	}));
}
else
{
	cboPortfolio._lookUpEdit.EditValue = o;
}
  • ปัญหาที่พบ Code ในเคสที่ต้อง delegated จาก Method InvokeRequired() มันเหมือนกันเลยครับ !!!!
  • จะว่าไปมันก็ขัดกับหลัก DRY (don’t repeat yourself) ครับ
  • แนวทางแก้ไข - ทำ Extensions Method ของ Control ขึ้นมาสิ เพื่อให้ Dev ไม่ต้อง Copy Code ซ้ำๆ ดังนี้
public static class ControlExtensions
{
	public static void InvokeIfRequired(this ISynchronizeInvoke obj
									  , MethodInvoker action)
	{
		if (obj.InvokeRequired)
		{
			var args = new object[0];
			obj.Invoke(action, args);
		}
		else
		{
			action();
		}
	}
}

Reference


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts to your email.