C# 跨线程更新UI控件

现象

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

分析

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
button.Invoke(new EventHandler(delegate
{
button.Text="关闭";
}));

listBox1.Invoke(new Action<string>((msg) =>
{
listBox1.Items.Add(DateTime.Now + ":" + msg);
}), str);
//3、使用委托与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控件常用方法汇总

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫中眸zc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值