2013-12-18 44 views
0

我有一個程序在一個窗口內的其他線程中運行一系列方法,讓我們的用戶知道使用狀態欄發生了什麼。狀態欄更新位於設置狀態欄的主線程中,然後刷新GUI。有串聯的代碼幾個街區每個看起來像這樣:多線程C#程序中不可靠的GUI更新

Thread do1Thread = new Thread(Class.Method); 
do1Thread.Start(); 
// inform user 
this.status.Text = "Doing stuff 1..."; 
// update GUI 
Utility.RefreshGUI(); 
// join thread 
do1Thread.Join(); 

有時在狀態欄確實更新,但往往是直到最後的第一個狀態停留在它顯示最近的狀態。偶爾會堅持「準備就緒」。這是默認的。

請注意,兩個塊需要幾秒鐘,所以應該有時間來更新。此外,該程序使用GTK#爲GUI編寫的C#(Mono)。

如何確保GUI更新以反映更改?

+5

上面的代碼塊是否發生在UI線程上?如果這樣做,UI線程上的'.Join()'是你的問題。 –

+0

大家好,我患有嚴重的感冒,當我康復後,我會嘗試其中的一些建議。我感謝所有的幫助。 – Razick

回答

0

您必須使用觀察和可觀察模式。

EDITED:

它是代碼真的更好除法邏輯和視圖的部分。

以下是真實世界中如何使用的示例。 Pattern

+2

請稍微多一些。這可能是一個評論,但它有點太短的答案... –

2

問題是Join()調用阻塞了阻止所有窗口消息的UI線程。

您可以使用BackgroundWorker並執行RunWorkerCompleted調用中的Join之後的任何代碼嗎?

+1

我有點不確定爲什麼使用'加入()'這裏是一個問題。 Join會導致當前線程等待它被調用的線程完成,對嗎?所以,由於下一次GUI更新直到連接之後*(只有兩個異步線程,GUI和進程線程)纔會出現,所以它不應該在此時阻止GUI。我誤解了什麼嗎? – Razick

+0

GTK#中沒有任何BackgroundWorker –

+3

@Razick你是誤解,你可能已經改變了GUI用來顯示的數據值,但是如果函數永遠不會返回到消息泵,它永遠不會有機會重新繪製屏幕新的信息。您必須將控制權返回給消息泵,而不是使用「加入」進行阻止,以便繪製事件發生。然後你可以通過一個'Event'來表明你的任務已經完成,並且讓這個事件開始下一步,而不是在'Join'上進行阻塞。 –

1

你需要派遣更新消息UI線程調用invoke而不是直接財產

this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen")); 
+0

運行此錯誤時出現錯誤:'錯誤CS1061:類型Gtk.Label'不包含BeginInvoke的定義,並且沒有找到類型爲Gtk.Label的擴展方法BeginInvoke'(您是否缺少using指令或大會參考?)' – Razick

+2

@Razick看看[這篇文章](http://stackoverflow.com/questions/2548200/how-do-i-access-gui-gtk-from-multi-threads)。 –

+1

@Razick,我已經添加轉換爲Action對不起,BeginInvoke方法需要Delegate,所以需要直接轉換。另外請注意,如果您提供的代碼在UI線程中,Join會阻止它,並且不會發生更新 –

1

我發現更新在主線程控制,最好的辦法是設置一個委託更新和調用來自其他線程。

+0

我會看看那個。我不知道如何使用代表,因此可能需要一段時間。 – Razick

-1

您可以檢查您是否使用StatusStrip控件? 如果是這樣,你的代碼看起來像直接設置Stautus地帶控制的文本

this.status.Text = "Doing stuff 1..."; 

所以它不會反映在狀態條爲文本。在這種情況下,您必須放置一個toolstriplabel並需要設置其文本。

請檢查帖子here

+0

問題是他阻止了UI線程,而不是他沒有正確地更改文本。 – Servy