CefSharp入門及使用過程中踩過的坑


以前公司里面都是用vs自帶的webbrowser來做項目的,前兩天老板突然跟我說,這個項目內存頗大,可不可以用CefSharp替換webbrowser來縮減內存。然后我就踏入了CefSharp的坑……在這里把CefSharp使用整理一下,留着萬一以后會看看呢~

這邊放一個小demo,里面包含了我想把Cef封裝成一個自定義控件然后用IE的調用方式調用最后沒有成功的想法和一個多線程調用隊列

https://github.com/ruoruoniao/CefSharpDemo

下面進入正題~

  • 添加依賴

 

 在nuget中搜索CefSharp,用wpf的就直接下載CefSharp.Wpf,用winform的就下載CefSharp.Winforms,其他的依賴項會自動下載,然后重新打開一下VS,不然智能感知一直出問題不跳匹配項~

 

  • 在設計器中添加ChromiumWebBrowser

最先需要用到CefSettings

 1 private void InitializeCefSettings()
 2 {
 3     //全局下Cef只能被初始化一次
 4     if(Cef.IsInitialized != true)
 5     {
 6         CefSettings settings = new CefSettings();
 7         //允許調用JS函數調用后端代碼
 8         CefSharpSettings.LegacyJavascriptBindingEnabled = true;
 9         CefSharpSettings.WcfEnabled = true;
10         //禁用代理設置
11         settings.CefCommandLineArgs.Add("no-proxy-server", "1");
12         //禁用硬件加速
13         settings.CefCommandLineArgs.Add("disable-gpu", "1");
14         Cef.Initialize(settings);
15     }
16 }

 

 

需要在你的控件或窗體初始化時加入這個函數,初始化Cef設置~

另外在加入這個函數的時候需要區分是否設計模式,不然設計器會拋出錯誤,代碼如下:

//winform版本
private bool IsDesignMode(System.Windows.Forms.UserControl ctl)
{
    bool returnFlag = false;

    #if DEBUG
    if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
    {
        returnFlag = true;
    }
    else if (Process.GetCurrentProcess().ProcessName == "devenv")
    {
        returnFlag = true;
    }
    #endif
    return returnFlag;
}

//wpf版本
private bool IsDesignMode(System.Windows.Controls.UserControl ctl)
{
    return System.ComponentModel.DesignerProperties.GetIsInDesignMode(ctl);
}

 

輸入參數根據你的需要變動,因為我這里是把他們放到自定義控件中了所以是UserControl,如果是窗體的話就是Form或者Window了~

在wpf中(如果同時有其他的winform控件,且有可能winform控件可能會和Browser重合的情況)用到CefSharp需要用到winform版本的CefSharp,不然會出現瀏覽器在最下層其他在上層蓋住瀏覽器,導致始終鼠標點不到瀏覽器的情況,很尷尬……

但是如果沒有其他winform control的話(webbrowser也算winform control)可以直接使用wpf的版本

不過據說winform的版本性能比wpf的版本好,也不知道真的假的

Wpf:

用之前需要先添加命名空間到xaml

<!--沒有其他winform control的情況,使用wpf版本-->
xmlns:CefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"

<!--有其他winform control的情況,使用Winform版本-->
xmlns:CefSharpForWinform="clr-namespace:CefSharp.WinForms;assembly=CefSharp.WinForms"

 

 

有winform control:

用winformHost包裹住winform版本的ChromiumWebBrowserBrowser直接dock=fill,位置顯示什么的操作外面的winformhost

 

<WindowsFormsHost x:Name="showshengciPanel"d:IsLocked="True">
<CefSharpForWinform:ChromiumWebBrowser x:Name="showshengci"Dock="Fill"/>
</WindowsFormsHost>

 

沒有winform control:

直接使用ChromiumWebBrowser

<CefSharp:ChromiumWebBrowser x:Name="WebBrowser" Loaded="LoadChromeBrowserComplete" FrameLoadEnd="LoadWebDocumentComplete" d:IsLocked="True"></CefSharp:ChromiumWebBrowser>

 

Winform:

winform的比較神奇,不可以添加在設計器中,只能在后台邏輯代碼中添加。

 

private void InitializeChromiumWebBrowser()
{
    //之前上面那個Cef設置初始化
    InitializeCefSettings();
    this.WebBrowser = new ChromiumWebBrowser("about:blank");
    this.WebBrowser.Dock = DockStyle.Fill;
    this.WebBrowser.Parent = this.WebBrowserPanel;
}

將這個函數丟到當前控件或窗體的構造函數中,並且加上區分設計模式的代碼,不然vs可能爆炸(停止運行),這個我也不知道為啥,但是確實出現過這種情況……按說應該是彈出設計器錯誤才對但是就直接炸掉了……

 

  • 在后台代碼中使用ChromiumWebBrowser 

常用功能

這里我把ie常用的功能調用方式也貼過來了,大家可以看一下當個對照

另外如果wpf與winform使用方式有區別我會寫出來,不寫的話就代表沒有區別都是這種調用方式

重定向

//IE
Webbrowser.Navigate("www.baidu.com");

//Cef
//wpf中
ChromiumWebBrowser.Address = "www.baidu.com";
//winform中
ChromiumWebBrowserForWinform.Load("www.baidu.com");

 

 

文檔加載完成調用方法

//IE
Webbrowser.LoadComplete += "LoadCompleted";
private void LoadCompleted(object sender, NavigationEventArgs e)
{
    //邏輯代碼
}

//Cef
ChromiumWebBrowser.FrameLoadEnd += "FrameLoadEnded";
private void FrameLoadEnded(object sender, FrameLoadEndEventArgs e)
{
    //邏輯代碼
}

 

調用頁面中的JS方法

//IE
Webbrowser.InvokeScript("Init", new object[]{"aa"});

//Cef調用無返回值時
ChromiumWebBrowser.ExecuteScriptAsync("Init", new object[]{ "aa"});//或者
ChromiumWebBrowser.ExecuteScriptAsync("Init('aa')");

//Cef調用需要返回值時(無返回值時也可使用)

//不等待返回值
ChromiumWebBrowser.EvaluateScriptAsync("Init", new object[]{"aa"});//或者
ChromiumWebBrowser.EvaluateScriptAsync("Init('aa')");

//等待返回值
Task<JavascriptResponse> result = ChromiumWebBrowser.EvaluateScriptAsync("Init", new object[]{"aa"});//用上面的第二種寫法也是可以的
string resultStr = result.Result.ToString();

 

 

網頁調用后台C#方法

//IE
Webbrowser.ObjectForScripting = o;

//Cef,只能在瀏覽器初始化的時候使用,不要在每一次頁面加載的時候都調用一次
ChromiumWebBrowser.JavascriptObjectRepository.Register("external", o, isAsync: false, options: new CefSharp.BindingOptions() { CamelCaseJavascriptNames = false });
//external隨你你想打啥打啥,如果你打一個jiuming在頁面中調用的時候就是jiuming.方法();
//后面那個BindingOptions()里面的東西是表示不使用駝峰命名法,也就是首字母為大寫的時候也能用
//如果不需要的話,options里面那一串可以用BindingOptions.DefaultBinder代替

 

取dom

//IE
HTMLDocument document = Webbrowser.Document;//可以直接操作
//Cef
IHTMLDocument2 document = (IHTMLDocument2)(new HTMLDocumentClass());
Task<JavascriptResponse> result = ChromiumWebBrowser.GetBrowser().MainFrame.GetSourceAsync();
document.write(result.Result.ToString());
return (HTMLDocument)document;//不可以直接操作就費這么大勁取出來只能讓你看看,Cef已經淡化了dom操作
  • 踩過的一些坑

  • 在FrameLoadEnd中調用需要等待返回值的JS函數時,如果直接使用上面的方法,會被卡死(我也沒搞明白為什么,有大佬能幫我看看嘛),所以需要采用以下方法
Task<JavascriptResponse> result = null;
result = ChromiumWebBrowser.EvaluateScriptAsync("Init('aa')");
var waiter = result.GetAwiter();
waiter.OnCompleted(() => {
    string resultStr = waiter.Result.ToString();
})

 

  • 如果想用dom操作的話,可以直接這樣子調用(如果在FarmeLoadEnd用上面寫的那種dom操作的話也會卡死~和等待返回值的JS函數調用一個道理):
//IE
dom.documentElement.style.overflow = "hidden";
//Cef
ChromiumWebBrowser.ExecuteScriptAsync("document.body.Style.overflow = 'hidden'");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM