您的位置:首页 > 产品设计 > UI/UE

C#在处理多线程更新到UI控件的多种方法

2012-08-30 20:49 375 查看
单线程的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();

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(); });

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: