前言
當客戶端需要進行網絡下載操作時如果只是簡單的用多線程這么一個操作而不給用戶知道當前的下載進度的話那么用戶將不知道已經下載了多少,甚至有可能直接關閉了主應用程序。那就杯具了。
那么如何在另外的線程中來更新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方法就可以實時的顯當前的下載進度了。
轉載自:https://www.cnblogs.com/smallerpig/p/3646230.html
