在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。
原因:調用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 }
關鍵字:客戶端瀏覽器進度Loading、IE瀏覽器loading、Cef瀏覽器loading