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

时间: 2024-09-20 06:00:39

Winfrom 如何安全简单的跨线程更新控件的相关文章

C# 跨线程调用控件

原文:C# 跨线程调用控件 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法   阅读目录 线程间操作无效 第一种办法:禁止编译器对跨线程访问做检查 第二种办法: 使用delegate和invoke来从其他线程中调用控件 第三种办法: 使用delegate和BeginInvoke来从其他线程中控制控件 第四种办法: 使用BackgroundWorker组件 源代码下载   线程间操作无效

windows跨线程调用控件的方法

用户不喜欢反应慢的程序.在执行耗时较长的操作时,使用多线程是明智之举,它可以提高程序 UI 的响应速度,使得一切运行显得更为快速.在Windows 中进行多线程编程曾经是 C++ 开发人员的专属特权,但是现在,可以使用所有兼容 Microsoft .NET 的语言来编写. 不过Windows 窗体体系结构对线程使用制定了严格的规则.如果只是编写单线程应用程序,则没必要知道这些规则,这是因为单线程的代码不可能违反这些规则.然而,一旦采用多线程,就需要理解 Windows 窗体中最重要的一条线程规则

C#中跨线程操作控件

一.前面的话 对于一些耗时型操作(如文件下载),让主线程去处理不是明智的选择,虽然这样做会使得程序开发起来很简单.因为WinForm程序设计的 准则之一就是Responsive,即让用户觉得程序一直在工作,而不是感觉它在罢工(呵呵,事实上,程序不会罢工,只是你没给他表现得机会, 如果它有情感,会觉得委屈死)..Net FrameWork支持在程序用应用线程编程,这可以很好的解决上述问题,不过有时候直接使用Thread和 Threadstart显得有些繁琐也没必要,为此.Net Framework

.net3.0中跨线程访问控件

这两天用WPF做一个项目的UI部分时, 发现跨线程地访问了UI控件, 自然地报异常了. 当时找了半天也没在控件中找到InvokeRequired属性和Invoke方法, 郁闷之极.....最后发现在.net3.0中,这有所改变了. 替代InvokeRequired的方法是DispatcherObject.CheckAccess()或DispatcherObject.VerifyAccess()方法,用于指示当前线程是否可以直接访问控件. 替代Invoke的方法是DispatcherObject.

多媒体定时器和跨线程更新窗口学习总结

总结了一些关于多媒体定时器的使用和处理跨线程更新窗口的原理和方法 微软在32位版本的系统里提供了一组所谓的"多媒体定时器"API,多媒体定时器可以使应用程序最大限度的获得硬件平台支持的定时精度.可以实现高精度的定时,例如可以应用于 MIDI序列发生器,MIDI时间产生的精度在一毫秒之内. 一.多媒体定时器的使用方法设置多媒体定时器timeSetEvent()函数,定时精度为ms级.利用该函数可以实现周期性的函数调用.1.函数的原型如下: MMRESULT timeSetEvent( U

C#子线程的控件操作问题解析

有关C#子线程的控件操作 一般来说,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线 程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是 不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了 Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作. 现在用一个用线程控制的进程条来说明,大致的步骤如下: 1.创建Invoke函数,大致如下: /// < summary> /// Delegate function to be invok

在c#中关于线程调用控件

由于c#不允许非创造控件线程调用控件,所以遇到过很多问题. 提示: 线程间操作无效: 从不是创建控件"showtable_btn"的线程访问它. 有些小郁闷 因为是刚刚使用thread 原始代码大概如下 private void showtable_btn_Click(object sender, EventArgs e) { thread t = new thead(s) t.start() }   private void s() { textbox1.text = "w

简单实现一个.net分页控件

最近写了一个.net的分页控件,放到园子里...你觉得好,就点个赞,不好呢,就告诉我为啥吧.... 是使用Request.QueryString的.... 参数: public int currentPageIndex = 0;//当前页数 public int pagesize = 16;//每页显示的条数 public int pagecount = 0;//页数 public int rowscount = 0;//总条数 public string prevtext = "前一页&quo

一个简单的工控管道控件

一.本人一直从事工控行业控制软件编程,经常要模拟物料传输过程,为了使效果更加生动,故借鉴VC知识库(VCKBASE.COM)中高人的一些代码,做了一个简单的控件,拿出来和大家一起分享.不足之处还请各位指点. 二.代码比较简单主要的部分是画管道,代码如下: void CMultiAxtiveX1Ctrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { CBrush brBk[64]; //创建画刷 int