C# Dispatcher.Invoke方法

這個東西的作用, 說白了就是輔線程不能更改UI的內容, 若要更改就需要用到這個東西。
下面內容來自https://www.cnblogs.com/smallerpig/p/3646230.html

前一篇小豬分享過在WPF中簡單的使用BackgroundWorker完成多線程操作!在那篇中小豬利用了BackgroundWorker組件對耗時比較多的操作放在了單獨的BackgroundWorker裏來完成,例如說:網絡請求的登錄操作,說到網絡請求當然還有另外一種請求:網絡下載。

當客戶端需要進行網絡下載操作時如果只是簡單的用多線程這麼一個操作而不給用戶知道當前的下載進度的話那麼用戶將不知道已經下載了多少,甚至有可能直接關閉了主應用程序。那就杯具了。

這時候就涉及到在另外的線程中來更新UI,但是WPF卻明確的規定:UI元素只能由其主線程來操作,其他任何線程都不可以直接操作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方法就可以實時的顯當前的下載進度了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章