最近幾天都被基於cefSharp封裝的瀏覽器控件搞瘋了!對於cefSharp基本滿足當前所做項目的需求,但是有一個問題一直困擾我,那就是系統中偶爾會出現輸入法不能轉換到中文。而且這個問題似乎沒有什么規律。
【項目需求】
先說一下項目對瀏覽器控件的需求,如果沒有需要做聽音這個功能,其實項目可以是一個B/S架構。但是由於現在需要聽音功能,所以決定使用C/S的架構。其中客戶端采用瀏覽器控件來實現網頁加載。客戶端主窗口內嵌一個瀏覽器控件,然后頁面有需要聽音的時候可以點擊聽音然后彈出聽音窗口。聽音窗口又是由一個音頻播放器+瀏覽器控件組成。
【最初實現】
最初使用的是webBrowser控件,能夠實現功能,但是webBrowser有太多坑,不斷出現內存泄露和根據客戶端IE版本進行樣式渲染,HTML5兼容性差等等,諸多問題。於是痛下狠心,決定換基於webkit的瀏覽器控件。
【痛苦的嘗試】
.Net中 基於webKit 的瀏覽器控件還是很多,於是做了一下各個版本的嘗試:
- webKit.Net 0.5 : 該控件內核比較老,目前最新還是2010,該控件唯一的優勢是和webBrowser使用比較接近。但是項目中使用了Angularjs,在每次取數據超過100條時,速度變慢。果斷pass。
- OpenWebKitSharp :該項目是基於webKit.Net的一次封裝。該控件對系統的功能能很好的滿足,但是與我們的系統有一個致命的不兼容性那就是內存泄露。
- cefSharp:基於cef1,能夠滿足項目的基本需求所以選擇了該控件。
【輸入法異常】
選擇了cefSharp后,在使用中出現一個讓人崩潰的問題,那就是輸入法偶爾出現不能轉換到中文。而且不好定位問題出現在什么位置。褲褲折騰一周終於找到原因。
基於cefSharp1自己封裝了一個瀏覽器控件,代碼如下(有刪減):
namespace XXXXXX.UserCtrol { using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using CefSharp.WinForms; using CefSharp; using System.IO; public partial class WebKitBrowser : UserControl { #region 字段 /// <summary> /// webView /// </summary> private WebView _Core; /// <summary> /// 網頁地址 /// </summary> private string _Url; /// <summary> /// CEF環境設置 /// </summary> private static Settings cefSettings; #endregion #region 構造函數 /// <summary> /// 構造函數 /// </summary> public WebKitBrowser() : this("about:blank") { } /// <summary> /// 構造函數 /// </summary> /// <param name="url">地址</param> public WebKitBrowser(string url) : this(url, new BrowserSettings()) { } /// <summary> /// 構造函數 /// </summary> /// <param name="url">地址</param> /// <param name="settings">瀏覽器設置</param> public WebKitBrowser(string url, BrowserSettings settings) { InitializeComponent(); _Url = url; this._Core = new WebView(url, settings); this._Core.Dock = DockStyle.Fill; this.Controls.Add(this._Core); BindEvents(); } #endregion #region 方 法 #region 公有方法 /// <summary> /// 頁面導航 /// </summary> /// <param name="url">地址</param> public void Navigate(string url) { if (string.IsNullOrWhiteSpace(url)) { this._Core.Load("about:blank"); } else { this._Core.Load(url); } } /// <summary> /// JS交互對象 /// </summary> /// <param name="obj">交互對象</param> public void ObjectForScript(object obj) { this._Core.RegisterJsObject("external", obj); } /// <summary> /// 調用JS方法 /// </summary> /// <param name="funcName">函數名</param> /// <param name="args">參數</param> /// <param name="isAsync">是否異步</param> /// <returns>返回值</returns> public object CallJavaScriptMethod(string funcName, object[] args, bool isAsync = false) { StringBuilder sparam = new StringBuilder(); sparam.Append(funcName).Append("("); if (args != null) { for (int i = 0; i < args.Length; i++) { object o = args[i]; if (i > 0) { sparam.Append(","); } if (o is string) { sparam.Append("\"").Append(o.ToString().Replace("\"", "'")).Append("\""); } else { sparam.Append(o); } } } sparam.Append(")"); try { if (isAsync) { this._Core.ExecuteScript(sparam.ToString()); return null; } else { return this._Core.EvaluateScript(sparam.ToString()); } } catch (Exception ex) { return null; } } /// <summary> /// 綁定下載控制器 /// </summary> /// <param name="hanler">下載控制類</param> public void BindDownLoadHandler(IRequestHandler hanler) { this._Core.RequestHandler = hanler; } /// <summary> /// 綁定快捷鍵菜單 /// </summary> /// <param name="Handler">菜單控制類</param> public void BindMenuHandler(IMenuHandler handler) { this._Core.MenuHandler = handler; } /// <summary> /// 綁定頁面加載控制器 /// </summary> /// <param name="handler">控制器</param> public void BindLoadHandler(ILoadHandler handler) { this._Core.LoadHandler = handler; } /// <summary> /// 綁定JS彈出框控制器 /// </summary> /// <param name="handler">控制器</param> public void BindJSDialogHandler(IJsDialogHandler handler) { this._Core.JsDialogHandler = handler; } /// <summary> /// 資源釋放 /// </summary> public void Dispose() { if(this._Core !=null) { this._Core.Dispose(); } } #endregion #region 私有方法 /// <summary> /// 綁定事件 /// </summary> private void BindEvents() { this._Core.LoadCompleted += new LoadCompletedEventHandler(_Core_LoadCompleted); this._Core.PropertyChanged += new PropertyChangedEventHandler(_Core_PropertyChanged); } #endregion #endregion #region 事 件 /// <summary> /// 瀏覽器加載完成事件 /// </summary> public EventHandler<LoadCompletedEventArgs> DocumentCompletedEvent; /// <summary> /// 屬性改變事件 /// </summary> public EventHandler<PropertyChangedEventArgs> DocumentPropertyChangedEvent; /// <summary> /// 加載完成事件 /// </summary> /// <param name="sender"></param> /// <param name="url"></param> private void _Core_LoadCompleted(object sender, LoadCompletedEventArgs url) { if (DocumentCompletedEvent != null) { DocumentCompletedEvent(sender, url); } } /// <summary> /// 屬性改變事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void _Core_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (DocumentPropertyChangedEvent != null) { DocumentPropertyChangedEvent(sender, e); } } #endregion #region 屬 性 /// <summary> /// 獲取瀏覽器地址 /// </summary> public string Url { get { return this._Url; } } /// <summary> /// tooltip內容 /// </summary> public string TooltipText { get { return this._Core.TooltipText; } set { this._Core.TooltipText = value; } } #endregion } }
主窗口使用了該控件,聽音窗口同樣使用了該控件。
【異常規律】
在不斷測試使用系統中發現,輸入法只有在win7下出現異常,在Xp系統下正常。且出現輸入法失敗,基本都是在打開聽音窗口后關閉窗口系統就出現輸入法異常。而關閉子窗口調用了自己封裝的Dispose()方法,及:
/// <summary> /// 資源釋放 /// </summary> public void Dispose() { if(this._Core !=null) { this._Core.Dispose(); } }
去除該代碼后。關閉聽音窗口調用控件自帶Dispose()方法,異常解決。O(∩_∩)O~
至於顯示釋放資源為什么在win7系統下就出現異常,還需去看看WebView 的Dispose()方法到底釋放了什么資源,導致輸入法出錯,也希望群里面大牛知道的告訴一下。