C# 跨线程更新UI控件

现象

“线程间操作无效: 从不是创建控件的线程访问它”

分析

C#Winform编程中,不支持跨线程直接更新UI控件。

解决方案

方案1:设置窗体属性,取消线程安全检查。

缺点:非线程安全,建议不使用

//指定不再捕获对错误线程的调用
Control.CheckForIllegalCrossThreadCalls = false;

方案2:使用上下文SynchronizationContext

/// 
/// 第一步:UI线程的同步上下文
/// 
SynchronizationContext m_SyncContext = null;
public Form1()
{
	InitializeComponent();
    //获取UI线程同步上下文
    m_SyncContext = SynchronizationContext.Current;
}
//第二步:定义更新UI控件的方法
/// 
/// 更新文本框内容的方法
/// 
/// 
private void SetTextSafePost(object text)
{
	this.textBox1.Text = text.ToString();
}
//第三步:定义线程的调用方法
/// 
/// 线程调用方法
/// 
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控件的方法
/// 
/// 更新文本框内容的方法
/// 
/// 
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");
    }
}

参考博文:
C# Winform 跨线程更新UI控件常用方法汇总

你可能感兴趣的:(C#,c#)