在windows開發界面時,使用浏覽器來請求和顯示網頁内容,是比較常見的。
但是在請求網頁内容時,因網速或者前端功能複雜加載較慢,亦或者加載時遇到各種問題,如空白/黑屏/加載不完整/證書問題等。
是以需要一個加載進度/加載失敗的顯示界面。
加載進度顯示
界面顯示
1. 界面顯示,加載進度樣式可參考:
繞圈進度條2. 添加Loading狀态枚舉。不加載/加載中/加載失敗
1 public enum LoadingState
2 {
3 NotLoading,//正常的網頁内容界面
4 Loading, //加載進度顯示
5 Error, //加載失敗界面
6 }
在控件内添加LoadingState附加屬性,前端界面通過綁定此附加屬性來确定是否顯示,LoadingState的變更時,界面顯示則直接變更。
進度顯示處理
在封裝相應浏覽器後,針對三個事件DocumentCompleted、ProgressChanged、NavigateError作進度顯示的處理。
結束加載進度
- DocumentCompleted文檔加載完成後,結束加載進度。此事件隻有Navigate調用後,才會觸發
- ProgressChanged進度變更通知,Navigate、Refresh調用後都會觸發。因為Navigate調用後,同一進度會重複觸發ProgressChanged,ProgressChanged在Navigate調用時觸發并沒有任何意義,是以在DocumentCompleted之後再添加事件的訂閱,ProgressChanged隻開放給Refresh方法。
- 目前進度大于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 }