我有父窗體和子窗體。父表單包含一個dataGridView(帳戶列表),子表單允許用戶註冊一個帳戶。事件,GUI和線程
子窗體在單獨的線程上啓動。
當賬戶被註冊時,它被添加到SQL數據庫中,並且事件在父窗體訂閱的子窗體上觸發。父窗體然後更新dataGridView以從數據庫添加新值。
問題是,當試圖更新父窗體中的dataGridView時,事件被解僱我得到一個交叉線程錯誤。這是正常的行爲嗎?
我有父窗體和子窗體。父表單包含一個dataGridView(帳戶列表),子表單允許用戶註冊一個帳戶。事件,GUI和線程
子窗體在單獨的線程上啓動。
當賬戶被註冊時,它被添加到SQL數據庫中,並且事件在父窗體訂閱的子窗體上觸發。父窗體然後更新dataGridView以從數據庫添加新值。
問題是,當試圖更新父窗體中的dataGridView時,事件被解僱我得到一個交叉線程錯誤。這是正常的行爲嗎?
在你的處理器,你將不得不做一些工作,以使處理器背面的UI線程上......「規範」是使用InvokeRequired
和BeginInvoke
的以下模式:
private void OnChildFormSaysNewItemsHandler(object sender, EventArgs e)
{
// Bring on the UI thread
if (this.InvokeRequired)
{
Action<object, EventArgs> handler = OnChildFormSaysNewItemsHandler;
this.BeginInvoke(handler, sender, e);
return;
}
// Do the normal work...
}
在事件處理程序中會有這樣的事情嗎?this.Invoke((MethodInvoker)delegate fillData(); }); –
在進行「正常工作」部分之後,您將回到UI線程,因此您可以調用'FillData()'而不必擔心invoke(它通常會同步調用UI線程中的某些內容) 。 – Reddog
是的,這是正常的,觸發事件的線程是子線程表單。在此事件中運行在mainform中的代碼在子窗體線程中運行。所以這很正常。 我鼓勵你不要在不同的線程中啓動窗體,控件。留在主線程中。但是您可以使用BackgroundWorker或其他任何工具在另一個線程中執行內部進程。
是的,您不允許從其他線程訪問UI元素,而不是創建它們。您需要將呼叫整理到父窗體所在的線程(如果您使用winforms,則InvokeRequired是您的朋友)
此類行爲是預期的 - WinForms應用程序模型不是線程安全設計。 要與來自非UI線程的控件進行交互,請使用Control.Invoke或Control.BeginInvoke()方法。
例子:
void RefreshData()
{
// Refresh database here
}
void MyOtherThreadCallback()
{
this.BeginInvoke(new Action(RefreshData()))
}
你父窗體嘗試更新使用解僱事件的線程的網格視圖。這會導致交叉線程錯誤。爲了避免這種情況,您必須使用您創建控件的線程來更新控件。這通常使用代碼:
if(control.InvokeRequired)
{
control.Invoke(delegateToThisMethod)
}
並在delegateToThisMethod
您更新網格視圖。
對這一問題的好深入回答this question
你不列出您使用的是什麼版本,但檢查出MSDN上的BackgroundWorker Class,它使基本線程很容易。
你會想看看這些事件:
OnDoWork Raises the DoWork event.
OnProgressChanged Raises the ProgressChanged event.
OnRunWorkerCompleted Raises the RunWorkerCompleted event.
在DoWork的方法會發生什麼是一個單獨的線程(以及任何調用),但二者所創建的運行ProgressChanged和RunWorkerCompleted方法在UI線程上,因此可以更新UI元素。
與ui控件的任何交互操作都必須從創建控件的線程中完成。假設您使用win窗體,則需要使用Control.BeginInvoke/Invoke方法與另一個線程中創建的控件進行交互 – Loman