温故而知新: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(); });            
        }
    }
}

转载请注明来自菩提树下的杨过

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏岑玉海

WF追忆

  前一阵子学习了一下工作流,现在写个总结记录一下这个过程。要弄工作流,首先就要有个界面来画图,做web的,没办法,只能选择javascript和silverl...

2846
来自专栏Java职业技术分享

java爬虫带你爬天爬地爬人生,爬新浪

HttpClient是Apache Jakarta Common下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持...

1555
来自专栏JadePeng的技术博客

使用Mongodb 做对象缓存

mongodb高效的访问速度,用来快速存取数据再合适不过了,缓存神马的,可以用这个的 另外,有的时候,如果仅仅存储几条数据,单独去建立一张表代价太大,这个时候,...

4695
来自专栏草根专栏

asp.net web api 2.2 基础框架(带例子)

简介 这个是我自己编写的asp.net web api 2.2的基础框架,使用了Entity Framework 6.2(beta)作为ORM。 该模板主要采用...

5509
来自专栏hbbliyong

Extjs4处理后台json数据中日期和时间的方法

当ASP.NET后台使用JavaScriptSerializer这个组件将对象序列化为json,或者使用ScriptMethod特性的json      [Sc...

3867
来自专栏知识分享

关于Http 传输二维json

然后就在想是不是   String str = jsonObject1.toString();    jsonObject.put("param",str); ...

1154
来自专栏张善友的专栏

ASP.NET MVC Preview生命周期分析

做ASP.NET WebForm开发都知道,ASP.NET有复杂的生命周期,学习ASP.NET MVC就要深入理解它的生命周期。今天从CodePlex上下载了A...

2167
来自专栏技术之路

wpf RoutedUICommand 绑定

如果 自己设置按钮的快捷键就用后台绑定 读取自己设置的快捷键见方法2 方法1  <Window x:Class="CustomerShortcutsDemo.W...

2049
来自专栏林德熙的博客

WPF 省市县3级联动

请看前台代码,最重要的是通过省选择的元素来作为下一级的数据,于是选择第一个修改时,就会自动联动

2132
来自专栏.NET开发者社区

C# WINFORM通过委托和事件窗体间(跨窗体)传值(自定义事件参数)--实例详解

C# WINFORM通过委托和事件窗体间(跨窗体)传值(自定义事件参数)--实例详解 在C# WINFORM的日常开发中,我们通常需要遇到跨窗体传值这一问题,实...

6886

扫码关注云+社区

领取腾讯云代金券