解决多线程操作控件时可能出现的异常:“在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级”
“Windows 窗体”使用单线程单元 (STA) 模型,因为“Windows 窗体”基于本机 Win32 窗口,而 Win32 窗口从本质上而言是单元线程。STA 模型意味着可以在任何线程上创建窗口,但窗口一旦创建后就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。除了 Windows 窗体之外,.NET Framework 中的类使用自由线程模型。有关 .NET Framework 中的线程的信息,请参见线程处理。
STA 模型要求需从控件的非创建线程调用的控件上的任何方法必须被封送到(在其上执行)该控件的创建线程。基类
如果您在控件中为大量占用资源的任务使用多线程,则用户界面可以在背景线程上执行一个大量占用资源的计算的同时保持可响应。
------------------------------------------------------
下面直接给出我的实例程序,一个按钮是非创建线程直接调用 DataGrid 控件的数据绑定,将抛出异常;
另一个按钮是通过 Invoke 调用,将成功执行。
using System;
using System.Data;
using System.Threading;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace MultiThreadOperateControls
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private delegate void BindDataGridDelegate(); // 创建委托和委托对象
private BindDataGridDelegate bindDataGridDelegate;
Thread bindGridThread;
private System.Windows.Forms.Button btnErrorHandle;
private System.Windows.Forms.Button btnSuccessHandle;
private System.Windows.Forms.DataGrid dataGrid1;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
bindDataGridDelegate = new BindDataGridDelegate(BindDataGrid); // 实例化委托对象并指定调用的方法
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
BindDataGrid();
}
private void btnErrorHandle_Click(object sender, System.EventArgs e)
{
StopBindThread();
bindGridThread = new Thread(new ThreadStart(BindDataGrid)); // 直接调用非此线程创建的控件的操作 抛出异常
bindGridThread.Start();
}
private void btnSuccessHandle_Click(object sender, System.EventArgs e)
{
StopBindThread();
bindGridThread = new Thread(new ThreadStart(InvokeBindDataGrid)); // 通过委托调用,合法
bindGridThread.Start();
}
private void BindDataGrid()
{
int dataGridItemLenght = 5;
DataTable dt = new DataTable("DataGridSource");
dt.Columns.Add("RandomValue");
for(int i=0;i<dataGridItemLenght;i++)
{
Random r = new Random();
DataRow dr = dt.NewRow();
dr[0] = r.Next(1,500);
dt.Rows.Add(dr);
}
try
{
dataGrid1.DataSource = dt;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void InvokeBindDataGrid() // 调用非此线程创建的控件的操作必须用 Invoke 或 BeginInvoke .否则将抛出异常
{
dataGrid1.Invoke(bindDataGridDelegate,null);
}
private void StopBindThread()
{
if((bindGridThread != null)&&(bindGridThread.IsAlive))
{
bindGridThread.Abort();
bindGridThread.Join();
}
bindGridThread = null;
}
}
}
using System.Data;
using System.Threading;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace MultiThreadOperateControls
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private delegate void BindDataGridDelegate(); // 创建委托和委托对象
private BindDataGridDelegate bindDataGridDelegate;
Thread bindGridThread;
private System.Windows.Forms.Button btnErrorHandle;
private System.Windows.Forms.Button btnSuccessHandle;
private System.Windows.Forms.DataGrid dataGrid1;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
bindDataGridDelegate = new BindDataGridDelegate(BindDataGrid); // 实例化委托对象并指定调用的方法
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
BindDataGrid();
}
private void btnErrorHandle_Click(object sender, System.EventArgs e)
{
StopBindThread();
bindGridThread = new Thread(new ThreadStart(BindDataGrid)); // 直接调用非此线程创建的控件的操作 抛出异常
bindGridThread.Start();
}
private void btnSuccessHandle_Click(object sender, System.EventArgs e)
{
StopBindThread();
bindGridThread = new Thread(new ThreadStart(InvokeBindDataGrid)); // 通过委托调用,合法
bindGridThread.Start();
}
private void BindDataGrid()
{
int dataGridItemLenght = 5;
DataTable dt = new DataTable("DataGridSource");
dt.Columns.Add("RandomValue");
for(int i=0;i<dataGridItemLenght;i++)
{
Random r = new Random();
DataRow dr = dt.NewRow();
dr[0] = r.Next(1,500);
dt.Rows.Add(dr);
}
try
{
dataGrid1.DataSource = dt;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void InvokeBindDataGrid() // 调用非此线程创建的控件的操作必须用 Invoke 或 BeginInvoke .否则将抛出异常
{
dataGrid1.Invoke(bindDataGridDelegate,null);
}
private void StopBindThread()
{
if((bindGridThread != null)&&(bindGridThread.IsAlive))
{
bindGridThread.Abort();
bindGridThread.Join();
}
bindGridThread = null;
}
}
}
-----------------------------------------------------------------