温故而知新:WinForm/Silverlight多线程编程中如何更新UI控件的值

单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么做,比如:

private void btnSet_Click(object sender, EventArgs e)
{    
    Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
    //当然也可以用匿名委托写成Thread t = new Thread(SetTextBoxValue);
    t.Start("Hello World");
}

void SetTextBoxValue(object obj) 
{
    this.textBox1.Text = obj.ToString();
}

 运行时,会报出一个无情的错误:
线程间操作无效: 从不是创建控件“textBox1”的线程访问它。

究其原因,winform中的UI控件不是线程安全的,如果可以随意在任何线程中改变其值,你创建一个线程,我创建一个线程,大家都来抢着更改"TextBox1"的值,没有任何秩序的话,天下大乱...

解决办法:
1.掩耳盗铃法(Control.CheckForIllegalCrossThreadCalls = false;)--仅Winform有效

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {        

        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;//这一行是关键      
        }
       

        private void btnSet_Click(object sender, EventArgs e)
        {           
            Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
            t.Start("Hello World");
        }

        void SetTextBoxValue(object obj) 
        {
            this.textBox1.Text = obj.ToString();
        }        
    }
}

设置Control.CheckForIllegalCrossThreadCalls为false,相当于不检测线程之间的冲突,允许各路线程随便乱搞,当然最终TextBox1的值到底是啥难以预料,只有天知道,不过这也是最省力的办法

2.利用委托调用--最常见的办法(仅WinForm有效)

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        delegate void D(object obj);

        public Form1()
        {
            InitializeComponent();            
        }
       

        private void btnSet_Click(object sender, EventArgs e)
        {           
            Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
            t.Start("Hello World");
        }

        void SetTextBoxValue(object obj) 
        {
            if (textBox1.InvokeRequired)
            {
                D d = new D(DelegateSetValue);
                textBox1.Invoke(d,obj);

            }
            else 
            {
                this.textBox1.Text = obj.ToString();
            }
        }

        void DelegateSetValue(object obj) 
        {
            this.textBox1.Text = obj.ToString();
        }
    }
}

3.利用SynchronizationContext上下文 -- 最神秘的方法(Winform/Silverlight能用)

之所以说它神秘,是因为msdn官方对它的解释据说也是不清不楚

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();            
        }       

        private void btnSet_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ParameterizedThreadStart(Run));
            MyPram _p = new MyPram() { context = SynchronizationContext.Current, parm = "Hello World" };
            t.Start(_p);
        }

        void Run(object obj) 
        {
            MyPram p = obj as MyPram;
            p.context.Post(SetTextValue, p.parm);
        }

        void SetTextValue(object obj) 
        {
            this.textBox1.Text = obj.ToString();
        }
    }

    public class MyPram 
    {
        public SynchronizationContext context { set; get; }
        public object parm { set; get; }
    }
}

4.利用BackgroundWorker --最偷懒的办法(Winform/Silverlight通用)

BackgroundWorker会在主线程之外,另开一个后台线程,我们可以把一些处理放在后台线程中处理,完成之后,后台线程会把结果传递给主线程,同时结束自己。

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();            
        }       

        private void btnSet_Click(object sender, EventArgs e)
        {
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
            using (BackgroundWorker bw = new BackgroundWorker())
            {
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                bw.RunWorkerAsync("Hello World");
            }
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
            e.Result = e.Argument;//这里只是简单的把参数当做结果返回,当然您也可以在这里做复杂的处理后,再返回自己想要的结果(这里的操作是在另一个线程上完成的)
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了
            this.textBox1.Text = e.Result.ToString();
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
        }       
    }    
}

5.Dispatcher.BeginInvoke--Silverlight的独门秘籍 

代码

using System.Threading;
using System.Windows.Controls;
using System.Windows.Input;

namespace ThreadTest
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Thread t = new Thread(SetTextValue);
            t.Start("Hello World");
        }

        void SetTextValue(object text) 
        {
            this.Dispatcher.BeginInvoke(() => { this.txt.Text = text.ToString(); });            
        }
    }
}

 

转载请注明来自菩提树下的杨过http://www.cnblogs.com/yjmyzz/archive/2009/11/25/1610253.html

时间: 2024-09-19 15:48:29

温故而知新:WinForm/Silverlight多线程编程中如何更新UI控件的值的相关文章

C# 后台线程更新UI控件

/********************************************************************************* * C# 后台线程更新UI控件 * 说明: * C#多线程更新UI控件的方法,每次都要找,记录一下,方便检索. * * 2017-10-23 深圳 南山平山村 曾剑锋 ********************************************************************************/ 一

一起谈.NET技术,ASP.NET MVC 2中使用jQuery UI控件详解

问:我想给我的ASP.NET MVC输入表单添加一个日期选择控件,但模型-视图-控制器(MVC)并没有提供这样的辅助方法,我该如何添加控件? 答:和ASP.NET Web表单不一样,MVC架构没有提供可以在设计面板中拖放的有状态的服务端控件,相反,MVC鼓励使用简单的HTML布局元素和基于数据的标签作为页面布局的要素,功能和最终的布局用客户端JavaScript和CSS样式表控制. MVC提供了一套基于HtmlHelper的扩展方法渲染大部分HTML标签,对于更复杂的功能,你需要自己编写HTML

ASP.NET MVC 2中使用jQuery UI控件详解

问:我想给我的ASP.NET MVC输入表单添加一个日期选择控件,但模型-视图-控制器(MVC)并没有提供这样的辅助方法,我该如何添加控件? 答:和ASP.NET Web表单不一样,MVC架构没有提供可以在设计面板中拖放的有状态的服务端控件,相反,MVC鼓励使用简单的HTML布局元素和基于数据的标签作为页面布局的要素,功能和最终的布局用客户端JavaScript和CSS样式表控制. MVC提供了一套基于HtmlHelper的扩展方法渲染大部分HTML标签,对于更复杂的功能,你需要自己编写HTML

Webform 异步怎么更新控件的值

问题描述 如题,我使用的异步编程模型,在返回结果后要更新UI上面控件的值,应该怎么实现呢 解决方案 解决方案二:System.Messaging.MessageQueuemyQueue=newMessageQueue(".\private$\myqueue");myQueue.ReceiveCompleted+=newReceiveCompletedEventHandler((Objectsource,ReceiveCompletedEventArgsasyncResult)=>

事件对象在多线程编程中的应用

本课中我们将要学习事件对象以及如何在多线程编程中如何使用同步对象. 理论:上一课中我们演示了如何用WINDOWS消息在不同的线程之间进行通讯.另外的两种,即:使用全局变量和事件对象,将在本课中讲解. 事件对象就像一个开关:它只有两种状态---开和关.当一个事件处于"开"状态,我们称其为"有信号"否则称为"无信号".您可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是"无信号"就让该线程睡眠,这样该线程占用的C

C++ Builder多线程编程中一些体会

最近在写一个程序用到了多线程,所以对CB下的多线程有一定的学习. 现在把自己的一些心得讲一下.水平有限,写的很粗略,请大家见谅. CB相对于VC来说,在CB下写多线程程序是很简单的.不仅是VCL中有TThread这个类.封装了那些关于多线程的WINDOW API.我觉得更方便的是他提供了 直接访问主VCL线程中对象的能力.可以很容易的和主线程中的窗体,控件 打交道.和单线程的方式没有太多区别.只是在有多个线程都要访问主线程 中的对象(比如访问同一个窗体上的StringGrid).只要用Threa

多线程编程中的本质问题

在编写多线程程序的时候我们经常需要判断临界条件,如对象池中的可用数,某一个对象是否可用等状态.当我们这么做的时候其实就已经错了,为什么这么说因为在我们的潜意识里一直在使用绝对时间点来判定多线程程序.因为我们习惯性的用锁然后判定当前时间点的临界条件状态并作出相应处理.这样做当然可以但是在理念上错了,锁是为了同步资源用的而不是临界条件(当然你们做也没关系不过我向往无锁并发).那我们不依赖绝对时间点还能依赖什么呢?那就是事件,不过这个event可不是.net里的那个关键字.这是个逻辑上的概念.所谓多线

多线程编程中如何唯一标识每个TWIN读卡器?

问题描述 多线程编程中如何唯一标识每个TWIN读卡器? 多线程编程的过程中发现,每次连接TWIN读卡器(PCSC)获取到的读卡器名不固定,如果连接十个以上的读卡器,就无法确定哪个线程与哪个读卡器对应,有没有什么办法能解决这个问题?或者能否通过读卡器的设备范例ID获取到读卡器的名字? 解决方案 你线程连接读卡器的时候,把线程id和读卡器名称保存到一个map中,这样以后要查询的时候,就可以根据线程id知道当前的读卡器名称 解决方案二: 这样会有问题的,这是由于PCSC的接口函数取到的读卡器名字是变化

对c++多线程编程中信号量的疑惑,求解

问题描述 对c++多线程编程中信号量的疑惑,求解 最近在写多线程,使用到信号量,对于信号量创建.释放createsemaphore和releasesemaphore中的初始资源数.最大并发数.计数增加个数不解,之前有用过,但都没有深入了解以至于现在模糊不清,最大并发数是指什么线程的最大并发数,自己线程or其他线程?增加的计数如果是2,是不是就相当于调用了两次waitfor函数?恳请熟悉这方面的朋友能给一个更为细致具体的讲解,小女不胜感激 解决方案 信号量就是限制同时只有1个或多个线程能够运行,数