Winfrom 如何安全简单的跨线程更新控件

来源: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

也可以参考:http://www.codeproject.com/Articles/37413/A-Generic-Method-for-Cross-thread-Winforms-Access#xx3867544xx

你可能感兴趣的:(ROM)