一般我們在實作多線程存取主線程控制項屬性時,必須以委派方式來存取,否則就有可能會出現 InvalidOperationException 例外, 一般初學者總會犯這個錯誤,大都不知道需要委派的動作,總是在子線程用一行 textBox1.Text = "aaaaa" 就想改變控制項的屬性,卻 換來 InvalidOperationException 的錯誤,而在一般情況下委派的動作總不會 像 textBox1.Text = "aaaaa" 短短一行就這麼直覺的解決問題,總覺得委派代碼實在麻煩。如果你運作的環境 是 .net framework 3.5(含以上),又覺得委派的代碼總是惹人厭的話,下面的代碼或許幫得上你解決惱人的委派問題,至少幫了我很多,分 享給大家,以下是實例。
首先定義一個類別,來當然是 Control Class 擴充方法類別。
public static class ExtensionControl { public static object GetPropertySafe(this Control control, string propertyName) { object returnValue = null; Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(propertyName, BindingFlags.GetProperty, null, control, null); }; if (control.InvokeRequired) { control.Invoke(func); } else { func(); } return returnValue; } public static object SetPropertySafe(this Control control, string propertyName, object value) { object returnValue = null; Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { value }); }; if (control.InvokeRequired) { control.Invoke(func); } else { func(); } return returnValue; } public static object GetPropertySafe(this ToolStripMenuItem control, string propertyName) { object returnValue = null; Control owner = control.Owner; Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(propertyName, BindingFlags.GetProperty, null, control, null); }; if (owner.InvokeRequired) { owner.Invoke(func); } else { func(); } return returnValue; } public static object SetPropertySafe(this ToolStripMenuItem control, string propertyName, object value) { object returnValue = null; Control owner = control.Owner; Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { value }); }; if (owner.InvokeRequired) { owner.Invoke(func); } else { func(); } return returnValue; } public static object InvokeMethodSafe(this Control control, string methodName, params object[] args) { object returnValue = null; if (args == null) { args = new object[1]; args[0] = null; } else if (args != null && args.Length == 0) { args = null; } Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, control, args); }; if (control.InvokeRequired) { control.Invoke(func); } else { func(); } return returnValue; } public static object InvokeMethodSafe(this ToolStripMenuItem control, string methodName, params object[] args) { object returnValue = null; if (args == null) { args = new object[1]; args[0] = null; } else if (args != null && args.Length == 0) { args = null; } Control owner = control.Owner; Action func = () => { Type type = control.GetType(); returnValue = type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, control, args); }; if (owner.InvokeRequired) { owner.Invoke(func); } else { func(); } return returnValue; } }
類別中分別定義 SetPropertySafe, GetPropertySafe, InvokeMethodSafe 方法,當作 是 Control 的擴充方法,在一個 WindowsFormsApplication 加入該代碼或者自己做成 dll 引用都可以,以下是多線程 實作委派的示例。
namespace WindowsFormsApplication1 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread thread = new Thread( ThreadStart => { //this.textBox1.Text = "Thread Edit"; //將上方會產生 InvalidOperationException 的代碼註解,改以下方代碼 this.textBox1.SetPropertySafe("Text", "ThreadEdit"); } ); thread.IsBackground = true; thread.Start(); } } }
這樣就徹底解決多線程存取主線程控制項委派的麻煩代碼,同樣一行程式碼做相同的事,接著就可以舉一反三再擴充 Control 方法,技術上並沒有多深,只是一個比較方便的代碼,希望能夠讓一些人在實作多線程委派時,能夠少走彎路,一同為程式加油。