在Winform中使用Invoke,修改主线程,并介绍Invoke与BeginInvoke的区别。

本文详细介绍了在子线程中修改主线程UI控件的正确方法,包括使用Invoke和BeginInvoke的区别,以及如何避免线程间操作无效的异常。通过示例代码展示了Invoke和BeginInvoke的使用场景,以及它们在同步和异步调用上的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、子线程修改主线程**

在主线程中开了一个子线程,如果要在子线程中修改主线程某个控件,会触发异常:“线程间操作无效: 从不是创建控件“button1”的线程访问它。”。
1.正确的写法是需要使用Invoke,Invoke方法需要创建一个委托。如下所示,我要修改一个Button控件的文字:

Thread testThread1=new Thread(new ThreadStart(process1));//主函数中创建一个子线程
testThread1.IsBackground = true;
testThread1.Start();


        void process1()
        {//使用委托
            this.Invoke(new EventHandler(delegate
            {
                button1.Text = "测试";
            }));
           
        }

2.使用Lamda表达式,在.Net Framework3.5及以后的版本中使用Action封装方法,代码如下:

void process1()
        {
            this.Invoke(new Action(()=>
            {
                button1.Text = "测试";
            }));
           
        }

3.一劳永逸的方法,在 InitializeComponent();下面加上下面这个语句,你就不需要使用上述介绍到的方法了,当是这样写会有什么后果,暂时还不清楚:

 System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//取消线程间的安全检查   解决线程间操作UI问题

一、Invoke和BeginInvoke的区别

上面介绍了Invoke的使用方法,接下来看看Invoke和BeginInvoke的区别到底怎么样???
(1)Invoke() 调用时,Invoke会阻止当前主线程的运行,等到 Invoke() 方法返回才继续执行后面的代码,表现出“同步”的概念。
(2)BeginInvoke() 调用时,当前线程会启用线程池中的某个线程来执行此方法,BeginInvoke不会阻止当前主线程的运行,而是等当前主线程做完事情之后再执行BeginInvoke中的代码内容,表现出“异步”的概念。

private void confirmButton1_Click(object sender, EventArgs e)     
{
     this.textBox1.Text = "2";
         this.Invoke(new EventHandler(delegate
         {
            this.textBox1.Text += "3";
         }));
            this.textBox1.Text += "4";
}
//得出的答案是234
private void confirmButton2_Click(object sender, EventArgs e)     
{
   this.textBox1.Text = "2";
   this.BeginInvoke(new EventHandler(delegate
   {
      this.textBox1.Text += "3";
    }));
   this.textBox1.Text += "4";
}
//得出的答案是243
当你在WinForm项目中遇到这个错误:“在创建窗口句柄之前,不能在控件上调用 InvokeBeginInvoke”,这是因为试图在控件尚未完全初始化或显示出来的时候就操作它们引发的。`Invoke` 和 `BeginInvoke` 是用于在UI线程外更新控件的方法,但是为了保证线程安全和UI响应,这些操作需要在控件已经添加到窗体有了窗口句柄之后才能执行。 解决这个问题的方法有几种: 1. **确保调用时间**:检查你的代码,确认是否在控件的构造函数、Load事件或其他早期处理阶段尝试了`Invoke`或`BeginInvoke`。应该在控件加载完成后执行这类操作。 ```csharp private void Form1_Load(object sender, EventArgs e) { // 这里可以安全地调用 InvokeBeginInvoke this.Invoke(new Action(() => YourMethodThatNeedsToBeInvoked())); } ``` 2. **异步等待**:如果你的操作是可延后的,考虑将它放入一个异步任务,在适当的时机(如`OnHandleCreated`事件)启动该任务。 ```csharp private async Task InitializeTask() { await Task.Delay(1); // 等待控件加载完成 // 现在可以安全地调用 await YourAsyncOperation(); } public partial class Form1 : Form { protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); InitializeTask().ConfigureAwait(false); // 启动任务 } } ``` 3. **事件订阅**:如果是在某个事件触发时需要操作控件,可以在事件处理器内部调用。 ```csharp button.Click += (sender, e) => { if (this.IsHandleCreated) { // 控制在此处是可用的 UpdateControls(); } else { // 如果尚未创建句柄,延迟处理 Invoke((Action)(() => UpdateControls())); } }; private void UpdateControls() { // 更新控件内容... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值