2008-10-14 74 views
6

我正在寫一個WinForms應用程序,它有兩種模式:控制檯或GUI。同一解決方案中的三個項目,一個用於控制檯應用程序,一個用於UI表單,第三個項目用於保存兩個界面都將連接的邏輯。控制檯應用程序運行非常順利。奇怪的跨線程UI錯誤

,其保持用戶選擇的模型,它有一個IList<T>其中T是一個局部對象,Step,它實現INotifyPropertyChanged,所以在UI這個被安裝到一個DataGridView。運行時一切正常,對象的初始狀態反映在屏幕上。

Step中的每一個對象都是依次執行的任務;一些屬性會改變,被反射回IList並傳遞給DataGridView。

UI版本中的此操作通過創建BackgroundWorker將事件返回到UI完成。 Step這樣做,並生成一個StepResult對象,它是一個枚舉類型,指示結果(例如Running,NotRun,OK,NotOK,Caveat)和一個字符串以指示消息(因爲該步驟運行但不完全如預期的那樣,即與一個警告)。通常這些操作將涉及數據庫交互,但在調試模式下,我會隨機生成一個結果。

如果消息爲null,從未有一個問題,但如果我生成這樣的迴應:

StepResult returnvalue = new StepResult(stat, "completed with caveat") 

我得到一個錯誤,指出在DataGridView正從比它的線程之外的其他線程訪問創建於。 (我通過一個自定義處理程序,它應該在需要時處理調用 - 也許它不?)

然後,如果我生成一個唯一的響應,例如使用隨機數r

StepResult returnvalue = new StepResult(stat, r.ToString()); 

的動作沒有問題成功,數字清晰地寫入到DataGridView。

我很困惑。我假設這是一個字符串文字問題,但任何人都可以提出一個更清晰的解釋?

回答

3

既然你是做UI通過活動簽約綁定,you might find this helpful ;這是我前一段寫的一個例子,它展示瞭如何繼承BindingList<T>,以便通知被自動整理到UI線程。

如果沒有同步上下文(即,控制檯模式),然後它恢復到簡單的直接調用,所以沒有開銷。在UI線程中運行時,請注意,這基本上使用Control.Invoke,它本身只是在UI線程上直接運行委託。所以只有任何開關,如果數據正在從非UI線程編輯 - 突然我們想要的; -p

4

你已經回答了你自己quesion: -

我得到一個錯誤,指出在DataGridView正從比它創建的線程以外的線程訪問。

WinForms堅持在表單和控件上執行的所有操作都是在創建表單的線程的上下文中完成的。原因很複雜,但與底層的Win32 API有很大關係。有關詳細信息,請參見The Old New Thing博客上的各種條目。

你需要做的是使用InvokeRequired和調用的方法,以確保控制始終從同一個線程(pseudocodeish)訪問:

object Form.SomeFunction (args) 
{ 
    if (InvokeRequired) 
    { 
    return Invoke (new delegate (Form.Somefunction), args); 
    } 
    else 
    { 
    return result_of_some_action; 
    } 
} 
+0

我很欣賞的InvokeRequired情況,但爲什麼當我通過一個唯一的字符串它的工作,而是傳遞一個硬連接字符串時不? – Unsliced 2008-10-14 08:18:59

+0

這兩個語句是從同一個地方在同一個環境中調用的嗎? – Skizz 2008-10-14 08:30:32

0

我發現這篇文章 - 「Updating IBindingList from different thread」 - 這指出怪給的BindingList手指 -

因爲的BindingList沒有設置用於異步操作,則必須更新從的BindingList與它所控制的線程相同。

明確地將父表單作爲ISynchronizeInvoke對象傳遞給對象,併爲BindingList<T>創建了包裝器。