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
- Control.InvokeRequired Property (System.Windows.Forms) | Microsoft Docs
- How to make thread-safe calls to controls - Windows Forms .NET | Microsoft Docs
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.