来源:http://www.cnblogs.com/rainbowzc/archive/2010/09/29/1838788.html
由于多线程可能导致对控件访问的不一致,导致出现问题。C#中默认是要线程安全的,即在访问控件时需要首先判断是否跨线程,如果是跨线程的直接访问,在运行时会抛出异常。
解决办法有两个:
1、不进行线程安全的检查
2、通过委托的方式,在控件的线程上执行
常用写法:(不安全)
private void WriteToolStripMsg(string msg, Color color) { if (this.InvokeRequired) { this.BeginInvoke(new MethodInvoker(delegate() { toolStripMsg.Text = msg; toolStripMsg.ForeColor = color; })); } else { toolStripMsg.Text = msg; toolStripMsg.ForeColor = color; } } private void btnLogin_Click(object sender, EventArgs e) { string userName = this.txtUserName.Text.Trim(); string pwd = this.txtPwd.Text.Trim(); if (userName.IsNullOrEmpty()) { WriteToolStripMsg("请输入登录名...", Color.Red); this.txtUserName.Focus(); return; } if (pwd.IsNullOrEmpty()) { WriteToolStripMsg("请输入密码...", Color.Red); this.txtPwd.Focus(); return; } if (userName.IsNotEmpty() && pwd.IsNotEmpty()) { WriteToolStripMsg("系统正在登陆中...", Color.Blue); this.btnLogin.BtnEnabled = false; string msg = string.Empty; Thread t = new Thread(() => { //判断用户登录是否成功。 string restulMsg = string.Empty; restulMsg = DataCenterService.Instance.Login(userName, pwd); if (restulMsg.IsNullOrEmpty()) { SysUser.CurrUserEntity = DataCenterService.Instance.GetInfoForName(userName); this.DialogResult = DialogResult.OK; } else { WriteToolStripMsg(restulMsg, Color.Red); this.BeginInvoke(new MethodInvoker(delegate() { this.btnLogin.BtnEnabled = true; })); } }); t.IsBackground = true; t.Start(); } }
上述写法并不是最安全的,存在一定的问题。
推荐写法:
delegate void UpdateShowInfoDelegate(System.Windows.Forms.TextBox txtInfo, string Info); /// <summary> /// 显示信息 /// </summary> /// <param name="txtInfo"></param> /// <param name="Info"></param> public void ShowInfo(System.Windows.Forms.TextBox txtInfo, string Info) { if (this.InvokeRequired) { //this.BeginInvoke(new MethodInvoker(delegate() //{ // txtInfo.AppendText(Info); // txtInfo.AppendText(Environment.NewLine + "\r\n"); // txtInfo.ScrollToCaret(); //})); Invoke(new UpdateShowInfoDelegate(ShowInfo), txtInfo,Info); return; } else { txtInfo.AppendText(Info); txtInfo.AppendText(Environment.NewLine + "\r\n"); txtInfo.ScrollToCaret(); } }
How to update the GUI from another thread in C#?
本文转载:http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c
跨线程时使用静态扩展方法更新控件
在CodeProject上看一个跨线程更新的方法,备忘一下。
如果在应用中存在较多简单的跨线程操作,下面的方法可能比较实用:
public static class ExtensionMethod { /// <summary> /// 有返回值的扩展方法 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="TResult"></typeparam> /// <param name="isi"></param> /// <param name="call"></param> /// <returns></returns> public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> call) where T : ISynchronizeInvoke { if (isi.InvokeRequired) { IAsyncResult result = isi.BeginInvoke(call, new object[] { isi }); object endResult = isi.EndInvoke(result); return (TResult)endResult; } else return call(isi); } /// <summary> /// 没有返回值的扩展方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="isi"></param> /// <param name="call"></param> public static void SafeInvoke<T>(this T isi, Action<T> call) where T : ISynchronizeInvoke { if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi }); else call(isi); } }
然后在使用时就可以使用匿名委托很方便的操作:
lblProcent.SafeInvoke(d => d.Text = textForLabel);
progressBar1.SafeInvoke(d => d.Value = i);
string
labelText = lblProcent.SafeInvoke(d => d.Text);
静态的扩展类方法使用泛型模板扩展像所有可继承 ISynchronizeInvoke 接口的控件,几乎适用于常见的所有控件呦 (来自 CodeProject 为所有类型的更新创建异步委托)
原始地址:http://www.codeproject.com/Articles/52752/Updating-Your-Form-from-Another-Thread-without-Cre