C#报错——(Winform) 在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级
问题点描述:
我新建一个线程,并在这个线程中,把某个控件的父级去掉或者更改,导致报这个异常
网上的解析如下:
“Windows 窗体”使用单线程单元 (STA) 模型,因为“Windows 窗体”基于本机 Win32 窗口,而 Win32 窗口从本质上而言是单元线程。STA 模型意味着可以在任何线程上创建窗口,但窗口一旦创建后就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。除了 Windows 窗体之外,.NET Framework 中的类使用自由线程模型。有关 .NET Framework 中的线程的信息,请参见线程处理。
STA 模型要求需从控件的非创建线程调用的控件上的任何方法必须被封送到(在其上执行)该控件的创建线程。基类 Control 为此目的提供了若干方法(Invoke、BeginInvoke 和 EndInvoke)。Invoke 生成同步方法调用;BeginInvoke 生成异步方法调用。
如果您在控件中为大量占用资源的任务使用多线程,则用户界面可以在背景线程上执行一个大量占用资源的计算的同时保持可响应。
用人话描述为:控件是属于主线程(UI线程),不可以跨线程修改其父级。
网上找到的答案是:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) {
//创建线程并调用方法CreateUI Thread t = new Thread(new ThreadStart(CreateUI)); t.Start(); }
// private void CreateUI() { AddTextBox(); } private void AddTextBox() { if (this.InvokeRequired) { this.Invoke(new MethodInvoker(delegate { AddTextBox(); })); return; } TextBox tb = new TextBox(); tb.Text = "test"; this.Controls.Add(tb); } }
看起来感觉很绕,而且很麻烦,又要新建方法,又要新建委托
所以我把它简化如下:
//使用拉姆达表达式创建一个委托,委托里面修改控件的父级
Action delega1 = () => { tabPageIO.Parent = null; tabPageIO.Parent = tabControlWav; }; //使用异步多线程更新 if (this.InvokeRequired) {
//新建一个线程,线程里面调用拉姆达表达式,拉姆达表达式里面使用异步的形式调用委托,委托里面再修改控件的父级 new Thread(() => this.Invoke(delega1)).Start(); } else { delega1(); }
拉姆达表达式真的是好东西,看起来很简洁