Dispatcher.Invok是WPF中特有的。使用方法如下
1)創建一個“WPF 引用程序”
2)在Window1.xaml中添加一個Label
3)在后台代碼Window1.xaml.cs中
using
System;
using
System.Windows;
using
System.Threading;
namespace
WpfApplication3
{
/// <summary>
/// Window1.xaml 的交互邏輯
/// </summary>
public
partial
class
Window1 : Window
{
public
Window1()
{
InitializeComponent();
// 啟動一個后台線程
Thread t =
new
Thread(WorkThread);
t.IsBackground =
true
;
t.Start();
}
// 后台線程
void
WorkThread()
{
while
(
true
)
{
// 利用Dispacther.Invoke調用更新labelClock顯示的內容
// 按WPF規定:labelClock是由主線程創建的。要想在后台線程
// 中刷新主線程創建的控件,必須通過Dispatcher.Invoke(...)
// 來實現!
this
.Dispatcher.Invoke(
new
Action(() => labelClock.Content
= DateTime.Now.ToString(
"HH:mm:ss"
)),
System.Windows.Threading.DispatcherPriority.Render);
// 后台線程停頓1秒
Thread.Sleep(1000);
}
}
}
}
|
4)運行結果
前一篇小豬分享過在WPF中簡單的使用BackgroundWorker完成多線程操作!在那篇中小豬利用了BackgroundWorker組件對耗時比較多的操作放在了單獨的BackgroundWorker里來完成,例如說:網絡請求的登錄操作,說到網絡請求當然還有另外一種請求:網絡下載。
當客戶端需要進行網絡下載操作時如果只是簡單的用多線程這么一個操作而不給用戶知道當前的下載進度的話那么用戶將不知道已經下載了多少,甚至有可能直接關閉了主應用程序。那就杯具了。
這時候就涉及到在另外的線程中來更新UI,但是WPF卻明確的規定:UI元素只能由其主線程來操作,其他任何線程都不可以直接操作UI。而實時的下載進度又不能通過調用某個回調函數來完成更新UI。
這時候就是Dispatcher.Invoke方法上場的時間了
下面代碼實現了我們想要的功能:
private delegate void SetTipsValue_dg(long solength, long stlength); private void SetTipsValue(long solength, long stlength) { block_Tips.Text = "下載中...." + solength + "/" + stlength; }
private bool DownloadFile(string URL, string filename) { try { System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(URL); System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse(); System.IO.Stream st = myrp.GetResponseStream(); long stll = myrp.ContentLength; System.IO.Stream so = new System.IO.FileStream(filename, System.IO.FileMode.Create); byte[] by = new byte[1024]; int osize = st.Read(by, 0, (int)by.Length); while (osize > 0) { long sol = so.Length; long stl = stll; this.block_Tips.Dispatcher.Invoke(new SetTipsValue_dg(SetTipsValue), sol, stl); so.Write(by, 0, osize); osize = st.Read(by, 0, (int)by.Length); } so.Close(); st.Close(); myrp.Close(); Myrq.Abort(); return true; } catch (System.Exception e) { return false; } }
上面代碼首先定義了一個委托:該委托接受兩個參數分別代表當前下載量和總下載量,然后定義了一個具體的實現該委托的方法,該方法調用UI來顯示數據。
在下載數據的主函數DownloadFile中調用了this.block_Tips.Dispatcher.Invoke方法並將實現了委托的方法SetTipsValue方法和當前下載量及總下載量的數值傳進去我們就完成了整個操作。這樣我們在下載數據的時候用另外的線程開啟了DownloadFile方法就可以實時的顯當前的下載進度了。
前言
當客戶端需要進行網絡下載操作時如果只是簡單的用多線程這么一個操作而不給用戶知道當前的下載進度的話那么用戶將不知道已經下載了多少,甚至有可能直接關閉了主應用程序。那就杯具了。
那么如何在另外的線程中來更新UI?
討論
WPF卻明確的規定:UI元素只能由其主線程來操作,其他任何線程都不可以直接操作UI。
WPF中的UI控件,如果我們探究本質,他們都是從DispatcherObject繼承,所以都必須由UI線程進行調度和使用,如果我們在其他的后台線程中操作界面相關的元素時,就會出現如下的異常信息:
調用線程無法訪問此對象,因為另一個線程擁有該對象。
這時候就是Dispatcher.Invoke方法上場的時間了。。。
解決
在 WPF 中,只有創建 DispatcherObject 的線程才能訪問該對象。 例如,一個從主 UI 線程派生的后台線程不能更新在該 UI 線程上創建的 Button 的內容。 為了使該后台線程能夠訪問 Button 的 Content 屬性,該后台線程必須將此工作委托給與該 UI 線程關聯的 Dispatcher。
使用 Invoke 或 BeginInvoke 來完成此操作。 Invoke 是同步操作,而 BeginInvoke 是異步操作。 該操作將按指定的 DispatcherPriority 添加到 Dispatcher 的事件隊列中。
代碼
private delegate void SetTipsValue_dg(long solength, long stlength);
private void SetTipsValue(long solength, long stlength)
{
block_Tips.Text = "下載中...." + solength + "/" + stlength;
}
private bool DownloadFile(string URL, string filename)
{
try
{
System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(URL);
System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse();
System.IO.Stream st = myrp.GetResponseStream();
long stll = myrp.ContentLength;
System.IO.Stream so = new System.IO.FileStream(filename, System.IO.FileMode.Create);
byte[] by = new byte[1024];
int osize = st.Read(by, 0, (int)by.Length);
while (osize > 0)
{
long sol = so.Length;
long stl = stll;
this.block_Tips.Dispatcher.Invoke(new SetTipsValue_dg(SetTipsValue), sol, stl);
so.Write(by, 0, osize);
osize = st.Read(by, 0, (int)by.Length);
}
so.Close();
st.Close();
myrp.Close();
Myrq.Abort();
return true;
}
catch (System.Exception e)
{
return false;
}
}
上面代碼首先定義了一個委托:該委托接受兩個參數分別代表當前下載量和總下載量,然后定義了一個具體的實現該委托的方法,該方法調用UI來顯示數據。
在下載數據的主函數DownloadFile中調用了this.block_Tips.Dispatcher.Invoke方法並將實現了委托的方法SetTipsValue方法和當前下載量及總下載量的數值傳進去我們就完成了整個操作。這樣我們在下載數據的時候用另外的線程開啟了DownloadFile方法就可以實時的顯當前的下載進度了。