天天看點

WPF 用戶端浏覽器 添加Loading加載進度 加載進度顯示浏覽器事件處理

原文: WPF 用戶端浏覽器 添加Loading加載進度

在windows開發界面時,使用浏覽器來請求和顯示網頁内容,是比較常見的。

但是在請求網頁内容時,因網速或者前端功能複雜加載較慢,亦或者加載時遇到各種問題,如空白/黑屏/加載不完整/證書問題等。

是以需要一個加載進度/加載失敗的顯示界面。

 加載進度顯示

界面顯示

1. 界面顯示,加載進度樣式可參考:

繞圈進度條

2. 添加Loading狀态枚舉。不加載/加載中/加載失敗

1     public enum LoadingState
2     {
3         NotLoading,//正常的網頁内容界面
4         Loading,   //加載進度顯示
5         Error,     //加載失敗界面
6     }      

在控件内添加LoadingState附加屬性,前端界面通過綁定此附加屬性來确定是否顯示,LoadingState的變更時,界面顯示則直接變更。

進度顯示處理

在封裝相應浏覽器後,針對三個事件DocumentCompleted、ProgressChanged、NavigateError作進度顯示的處理。

結束加載進度

  1. DocumentCompleted文檔加載完成後,結束加載進度。此事件隻有Navigate調用後,才會觸發
  2. ProgressChanged進度變更通知,Navigate、Refresh調用後都會觸發。因為Navigate調用後,同一進度會重複觸發ProgressChanged,ProgressChanged在Navigate調用時觸發并沒有任何意義,是以在DocumentCompleted之後再添加事件的訂閱,ProgressChanged隻開放給Refresh方法。
  3. 目前進度大于0時,立即結束Loading,減少延時。
1     /// <summary>
 2     /// 文檔加載完成
 3     /// </summary>
 4     /// <remark>Navigate方法觸發,Refresh方法不會觸發</remark>
 5     /// <remark>在首次加載時,添加DocumentCompleted訂閱</remark>
 6     /// <param name="sender"></param>
 7     /// <param name="e"></param>
 8     private void Browser_OnDocumentCompleted(object sender, HtmlDocumentCompletedEventArgs e)
 9     {
10         //目前Loading狀态不是Error的情況下,才結束加載
11         if (LoadingState == LoadingState.Loading)
12         {
13             LoadingState = LoadingState.NotLoading;
14         }
15 
16         //顯示網頁内容首次加載後,再訂閱加載進度事件
17         _browser.ProgressChanged -= Browser_ProgressChanged;
18         _browser.ProgressChanged += Browser_ProgressChanged;
19         OnDocumentCompleted(e);
20     }
21 
22     /// <summary>
23     /// 加載進度事件
24     /// </summary>
25     /// <remark>Navigate會觸發多次ProgressChanged事件,是以此事件訂閱不應開放給Navigate</remark>
26     /// <remark>Refresh調用後,會觸發一次</remark>
27     /// <param name="sender"></param>
28     /// <param name="e"></param>
29     private void Browser_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
30     {
31         //目前進度大于0,且目前Loading狀态是Loading的情況下,才結束Loading動畫
32         if (e.CurrentProgress > 0 && LoadingState == LoadingState.Loading)
33         {
34             LoadingState = LoadingState.NotLoading;
35         }
36     }      

值得注意的是,如果按照如上設定,當IE8環境更新到IE11後,因WebBrowserProgressChangedEventArgs事件參數傳回異常,不會結束Loading。

問題跟進記錄:

When IE updates from version 8 to version 11,this eventArgs value for event ProgressChanged is weird. 

原因:調用refresh方法。經調試發現ProgressedChanged的事件參數中,MaximumProgress一直等于0。

但是,正常情況下,同樣的電腦環境,win7 IE8或者IE11下,refresh方法,傳回的MaximumProgress不為0。

推薦分析:更新後,原有IE版本的注冊項遺留,導緻沖突。

解決方案:添加e.CurrentProgress == e.MaximumProgress的條件判斷。

1     private void Browser_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
 2     {
 3         //當以下倆種條件符合時,才結束Loading動畫
 4         //1.目前Loading狀态是Loading的情況下
 5         //2.目前進度大于0,或者目前進度等于進度上限閥值
 6         if (LoadingState == LoadingState.Loading && (e.CurrentProgress > 0 || e.CurrentProgress == e.MaximumProgress))
 7         {
 8             LoadingState = LoadingState.NotLoading;
 9         }
10     }      

PS:微軟小組成員推薦使用

WebView

,然而這個隻為Win10的Microsoft Edge開發的控件,隻能在win10上運作且隻支援.NET4.6.2及以上,限制多多。

加載出錯

1     private void Browser_NavigateError(object sender, BrowserExtendedNavigateErrorEventArgs e)
 2     {
 3         e.Cancel = true;
 4         LoadingState = LoadingState.Error;
 5 
 6         //目前不是網絡問題的異常,記錄異常日志
 7         if (e.StatusCode != NavigationErrorHttpStatusCode.INET_E_RESOURCE_NOT_FOUND)
 8         {
 9             Console.WriteLine($"WebBrowser無法連接配接伺服器:{e.Url},異常資訊為:{e.StatusCode},異常Code為:{ (int)e.StatusCode }");
10         }
11     }      

浏覽器事件處理

針對如上三個事件,DocumentCompleted、ProgressChanged、NavigateError

winform版IE浏覽器

1     /// <summary>
2     ///   在 <see cref="T:System.Windows.Forms.WebBrowser" /> 控件完成加載文檔時發生。
3     /// </summary>
4     [SRCategory("CatBehavior")]
5     [SRDescription("WebBrowserDocumentCompletedDescr")]
6     public event WebBrowserDocumentCompletedEventHandler DocumentCompleted;      
1     /// <summary>
2     ///   在 <see cref="T:System.Windows.Forms.WebBrowser" /> 控件已更新有關要導航到的文檔的下載下傳進度的資訊時發生。
3     /// </summary>
4     [SRCategory("CatAction")]
5     [SRDescription("WebBrowserProgressChangedDescr")]
6     public event WebBrowserProgressChangedEventHandler ProgressChanged;      

NavigateError加載失敗事件,需要重寫CreateSink、DetachSink。在對應的cookie中添加額外事件的處理:(在此不詳述,這個封裝子產品本人也不太了解~囧)

1     public void NavigateError(object pDisp, ref object url, ref object frame, ref object statusCode, ref bool cancel)
2     {
3         _browser?.NavigateError?.Invoke(this, new BrowserExtendedNavigateErrorEventArgs((string)url, (string)frame, (int)statusCode, cancel));
4     }      

Cef浏覽器

1     private void Register()
 2     {
 3         _cefBrowser.LoadingStateChanged += CefBrowserOnLoadingStateChanged;
 4         _cefBrowser.LoadError += CefBrowserOnLoadError;
 5     }
 6     private void CefBrowserOnLoadError(object sender, LoadErrorEventArgs args)
 7     {
 8         // Don't display an error for downloaded files where the user aborted the download.
 9         if (args.ErrorCode == CefErrorCode.Aborted)
10         {
11             return;
12         }
13         _isLoadError = true;
14         DispatcherUtil.Invoke(() =>
15         {
16             NavigateError?.Invoke(this, new BrowserExtendedNavigateErrorEventArgs(args.FailedUrl, args.Frame.Name, (int)args.ErrorCode, false));
17         });
18     }
19 
20     private void CefBrowserOnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
21     {
22         //isLoading為false,代表LoadingCompleted
23         //_isLoadError為false,代表未加載出錯
24         if (!args.IsLoading && !_isLoadError)
25         {
26             DispatcherUtil.Invoke(() =>
27             {
28                 DocumentCompleted?.Invoke(this, new HtmlDocumentCompletedEventArgs(null));
29             });
30         }
31     }      

繼續閱讀