網站首頁 語言 會計 互聯網計算機 醫學 學歷 職場 文藝體育 範文
當前位置:學識谷 > 範文 > 熱點

WPF怎樣在工作線程中更新窗體的UI元素Dispa

欄目: 熱點 / 發佈於: / 人氣:1.68W

這是一個普遍的問題:如果我們再程序中使用了多線程技術,而工作線程(後台線程)如果需要更新界面上的元素(例如進度條等),就會有一個線程安全性問題,因為進度條是由主線程創建出來的。

WPF怎樣在工作線程中更新窗體的UI元素Dispa

關於這一點,大致上看,WPF的機制與Windows Forms是沒有差別的。我們在Windows Forms中需要按照下面的方式更新窗體元素。

using System;using s;using ading;namespace WindowsFormsApplication1{public partial class Form1 : Form{public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){//錯誤的寫法:直接更新new Thread(() =>{e = 10;})t();}private void button2_Click(object sender, EventArgs e){//正確的寫法,通知主線程更新new Thread(()=>{ke(new Action(() =>{e = 10;}));})t();}}}

第一種是錯誤的寫法,它將導致一個運行時錯誤

瞭解了這些,我們來看看WPF是怎麼做的?

點擊第一個按鈕的話,我們同樣會收到一個錯誤

點擊第二個按鈕,則工作正常

我們看看代碼有什麼區別

using System;using ows;using ading;namespace WpfApplication1{///

/// 的交互邏輯///

public partial class Window1 : Window{public Window1(){InitializeComponent();}private void button1_Click(object sender, RoutedEventArgs e){//錯誤的寫法:直接更新new Thread(() =>{e = 20;})t();}private void button2_Click(object sender, RoutedEventArgs e){//正確的寫法:通知主線程去完成更新new Thread(()=>{ke(new Action(()=>{e=20;}));})t();}}}

請注意,Window類並沒有Invoke方法,這是與Form不一樣的。取而代之的是,我們需要通過訪問atcher屬性,然後調用Invoke方法 。僅此而已

好吧,那麼到底什麼是Dispatcher呢?從字面上來説,它是所謂的接線員,或者調度員的`意思。這説明什麼呢?每個線程都有一個唯一的調度員,我們在代碼中所做的工作其實是向這個調度員發出指令,然後它再幫我們做。這樣理解就對了。

我們的窗體是在主線程創建出來的,裏面的控件自然也是如此。我們之前解釋過WPF的應用程序也是單線程模型的(STAThread),所以整個應用程序裏面會有一個默認的Dispatcher,它負責調度主線程的工作。

其實,如果大家真有興趣,可以看看這個方法的實現,就能理解上面所説的話了

[SecurityCritical]internal int RunInternal(Window window){fyAccess();alTraceEvent(UNGUID, 0);if (this._appIsShutdown){throw new InvalidOperationException(("CannotCallRunMultipleTimes", new object[] { ype()Name }));}if (window != null){if (!kAccess()){throw new ArgumentException(("WindowPassedShouldBeOnApplicationThread", new object[] { ype()Name, ype()Name }));}if (!tem(window)){(window);}if (Window == null){Window = window;}if (bility != ble){nInvoke(, delegate (object obj) {(obj as Window)();return null;}, window);}}reHwndSource();if (!owserHosted){ispatcher(null);}return this._exitCode;}[SecurityCritical]private object RunDispatcher(object ignore){rt(!this._ownDispatcherStarted);this._ownDispatcherStarted = true;();return null;}那麼,為什麼在Window中可以調用到Dispatcher屬性呢?或者説在那些對象上面可以採用這種機制呢?大致上説,幾乎所有的控件都可以,因為他們的基類一般都可以追溯到一個DispatcherObject

從這個角度來説,如果我們自己編寫一個用於WPF的控件,那麼也是需要按照這樣的層次結構去繼承的。這樣在控件內部,才可以通過atcher來更新一些界面元素了。

那麼,如果我們是一個類庫項目,就是一個標準的類型,它也需要更新到主線程中的一些元素怎麼辦?

此時可以通過acther來實現,例如下面的例子

using System;using ows;namespace WpfApplication1{public class WindowHelper{public static void SomeMethod() {ke(new Action(() => {e = "我修改過的窗體標題";}));}}}

其實,要認真講的話,Application的這個Dispatcher與我們剛才用到的Window的Dispatcher是同一個對象。也就是説,每個線程只有一個。

而其實,使用Window的Dispatcher,與使用Button的Dispatcher也沒有區別的

最後要説一點的是,Dispatcher除了Invoke方法之外,還有BeginInvoke方法。區別在於後者是異步執行的。如何使用異步的機制呢?

using System;using ows;using ading;namespace WpfApplication1{public class WindowHelper{public static void SomeMethod() {//ke(new Action(() => {// e = "我修改過的窗體標題";//}));var task = nInvoke(new Action(() => { e = "我修改過的窗體標題"; }));leted += new EventHandler(task_Completed);}static void task_Completed(object sender, EventArgs e){("任務已經完成");}}}