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