解決多線程操作控件時可能出現的異常:“在某個線程上創建的控件不能成爲在另一個線程上創建的控件的父級”
------------------------------------------------------
“Windows 窗體”使用單線程單元 (STA) 模型,因爲“Windows 窗體”基於本機 Win32 窗口,而 Win32 窗口從本質上而言是單元線程。STA 模型意味着可以在任何線程上創建窗口,但窗口一旦創建後就不能切換線程,並且對它的所有函數調用都必須在其創建線程上發生。除了 Windows 窗體之外,.NET Framework 中的類使用自由線程模型。有關 .NET Framework 中的線程的信息,請參見線程處理。
STA 模型要求需從控件的非創建線程調用的控件上的任何方法必須被封送到(在其上執行)該控件的創建線程。基類 Control 爲此目的提供了若干方法(Invoke、BeginInvoke 和 EndInvoke)。Invoke 生成同步方法調用;BeginInvoke 生成異步方法調用。
如果您在控件中爲大量佔用資源的任務使用多線程,則用戶界面可以在背景線程上執行一個大量佔用資源的計算的同時保持可響應。
------------------------------------------------------
下面直接給出我的實例程序,一個按鈕是非創建線程直接調用 DataGrid 控件的數據綁定,將拋出異常;
另一個按鈕是通過 Invoke 調用,將成功執行。
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;
}
}
}