C# 跨线程更新UI控件
程序员文章站
2022-06-10 16:29:00
...
现象
“线程间操作无效: 从不是创建控件的线程访问它”
分析
C#Winform编程中,不支持跨线程直接更新UI控件。
解决方案
方案1:设置窗体属性,取消线程安全检查。
缺点:非线程安全,建议不使用
//指定不再捕获对错误线程的调用
Control.CheckForIllegalCrossThreadCalls = false;
方案2:使用上下文SynchronizationContext
/// <summary>
/// 第一步:UI线程的同步上下文
/// </summary>
SynchronizationContext m_SyncContext = null;
public Form1()
{
InitializeComponent();
//获取UI线程同步上下文
m_SyncContext = SynchronizationContext.Current;
}
//第二步:定义更新UI控件的方法
/// <summary>
/// 更新文本框内容的方法
/// </summary>
/// <param name="text"></param>
private void SetTextSafePost(object text)
{
this.textBox1.Text = text.ToString();
}
//第三步:定义线程的调用方法
/// <summary>
/// 线程调用方法
/// </summary>
private void ThreadProcSafePost()
{
//在线程中更新UI(通过UI线程同步上下文m_SyncContext)
m_SyncContext.Post(SetTextSafePost, "This text was set safely by SynchronizationContext-Post.");
}
方案3:使用Invoke/BeginInvoke方法更新
缺点:在Lock中无法包含Invoke/BeginInvoke,造成死锁。
//1、直接调用Invoke
this.Invoke(new Action(() =>{ //插入代码 }));
//2、使用委托与Invoke
// 第一步:定义委托类型
// 将text更新的界面控件的委托类型
delegate void SetTextCallback(string text);
//第二步:定义更新UI控件的方法
/// <summary>
/// 更新文本框内容的方法
/// </summary>
/// <param name="text"></param>
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)//如果调用控件的线程和创建创建控件的线程不是同一个则为True
{
while (!this.textBox1.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (this.textBox1.Disposing || this.textBox1.IsDisposed)
return;
}
SetTextCallback d = new SetTextCallback(SetText);
this.textBox1.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
方案4:使用BackgroundWorker对线程进行安全的访问
private BackgroundWorker m_BackgroundWorker;// 申明后台对象
public MainWindow()
{
InitializeComponent();
m_BackgroundWorker = new BackgroundWorker(); // 实例化后台对象
m_BackgroundWorker.WorkerReportsProgress = true; // 设置可以通告进度
m_BackgroundWorker.WorkerSupportsCancellation = true; // 设置可以取消
m_BackgroundWorker.DoWork += new DoWorkEventHandler(DoWork); //执行任务
m_BackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(UpdateProgress); //更新控件
m_BackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CompletedWork);//任务完成
m_BackgroundWorker.RunWorkerAsync(this);执行
}
//线程执行程序
void DoWork(object sender, DoWorkEventArgs e)
{
//在线程中更新UI(通过ReportProgress方法)
m_BackgroundWorker.ReportProgress(50, "This text was set safely by BackgroundWorker.");
}
void UpdateProgress(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.UserState.ToString();
}
void CompletedWork(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error");
}
else if (e.Cancelled)
{
MessageBox.Show("Canceled");
}
else
{
MessageBox.Show("Completed");
}
}