欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

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

相关标签: C# c#