使用CefSharp在.NET中嵌入Google kernel
使用cefsharp可以在.net轻松的嵌入html,不用担心wpf与winform 控件与它的兼容性问题,cefsharp大部分的代码是c#,它可以在vb或者其他.net平台语言中来进行使用。
近几天来,公司项目中需要使用webbrowser,其中考虑了几个控件,如1.winform中的webbrowser 2.wpf中的webbrowser 3.webkit.net 4.cefsharp
winform的webbrowser放到项目中进行了实验和处理,发现致命问题:allowstransparency = true 和 webbrowser 等内置窗体显示冲突,导致不发选择,还有就是gc回收不及时,一下子就飙到了100mb+.
后来考虑wpf的webbrowser 它实际上还是封装了winform,有个严重的问题就是它一直置顶到最顶层,so,无法选择。
再后来考虑webkit.net ,但发现已经n多年没有更新,所以不在考虑...
最后跌跌撞撞跑到cefsharp,发现非常的坑啊!!竟然不支持anycpu,关键是我的项目中有的功能是必须anycpu启动的啊!还好,官方在去年已经公布了支持anycpu的方法。
首先nuget引用cefsharp.wpf,它会自己引用common,其他不用管。我们还需要再app.xaml.cs中添加代码来支持anycpu。
public partial class app : application { public app() { //add custom assembly resolver appdomain.currentdomain.assemblyresolve += resolver; //any cefsharp references have to be in another method with noninlining // attribute so the assembly rolver has time to do it's thing. initializecefsharp(); } [methodimpl(methodimploptions.noinlining)] private static void initializecefsharp() { var settings = new cefsettings(); // set browsersubprocesspath based on app bitness at runtime settings.browsersubprocesspath = path.combine(appdomain.currentdomain.setupinformation.applicationbase, environment.is64bitprocess ? "x64" : "x86", "cefsharp.browsersubprocess.exe"); // make sure you set performdependencycheck false cef.initialize(settings, performdependencycheck: false, browserprocesshandler: null); } // will attempt to load missing assembly from either x86 or x64 subdir // required by cefsharp to load the unmanaged dependencies when running using anycpu private static assembly resolver(object sender, resolveeventargs args) { if (args.name.startswith("cefsharp")) { string assemblyname = args.name.split(new[] { ',' }, 2)[0] + ".dll"; string archspecificpath = path.combine(appdomain.currentdomain.setupinformation.applicationbase, environment.is64bitprocess ? "x64" : "x86", assemblyname); return file.exists(archspecificpath) ? assembly.loadfile(archspecificpath) : null; } return null; } private void application_startup(object sender, startupeventargs e) { } }
还没完,之后你得在你项目的 *.csproj 中添加一行配置,它是在你的第一个propertygroup中添加的。
最后一步,在app.config中配置x86,当然不是说不支持cpu,刚刚app.xaml.cs中我们已经修改了啊。
<configuration> <runtime> <assemblybinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatepath="x86"/> </assemblybinding> </runtime>
</...>
就现在我们在页面中添加一个cefsharpbrowser在grid中,您需要在容器空间中添加name 标记。
public static chromiumwebbrowser cefsha { get; set; } public visualization() { initializecomponent(); } private void page_loaded(object sender, routedeventargs e) { cefsha = new chromiumwebbrowser(environment.currentdirectory + "/../../pages/store/visualizationhtml/storedata.html"); // 页面加载完毕后打开开发者工具 cefsha.keyboardhandler = new cefkeyboardhander(); cefsharpsettings.legacyjavascriptbindingenabled = true; newmutlipage(); this.grid.children.add(cefsha);//初始化 }
前后端交互都是在newmutlipage中搭起的桥梁,该代码是新版本的配置方式,大概是18年底,前后端的钩子可以通过bound来偷取。
public void newmutlipage() { cefsha.javascriptobjectrepository.resolveobject += (s, eve) => { var repo = eve.objectrepository; if (eve.objectname == "bound") { repo.register("bound", new jsevent(), isasync: true, options: new bindingoptions() { camelcasejavascriptnames = false }); } }; }
老版本你可以这么配置前后端交互的桥梁。
public void oldsinglepage() { // 新版本默认为 false cefsharpsettings.legacyjavascriptbindingenabled = true; // 把 testclass 注入到单个页面,名称为 test _chromiumwebbrowser.registerjsobject("testold", new testclass()); }
legacyjavascriptbindingenabled 该属性就是让webbrowser与wpf支持js交互,否则不可以建立jsobject 这个对象。
在 registerjsobject 中我们是绑定的类,该类中是我们后台向webbrowser的所有数据方法。
public class jsevent { /// <summary> /// 根据货架列代码区查相关信息 /// </summary> /// <param name="sline_code"></param> public void getobjbysline_code(string sline_code) { visualization.cefsha.executescriptasync($@"getgoods({ newtonsoft.json.jsonconvert.serializeobject(visualizationdal.getlist(sline_code))});"); } /// <summary> /// 获取货架信息 /// </summary> /// <param name="sline_code"></param> public void getshelvedetail(string sline_code) { //1.找到那列的所有货架 observablecollection<store_shelve> shelvelist = xdal.store_shelve_dal.getlistbylinename(sline_code); //2.找到关于该列下所有的商品信息 observablecollection<store_detailvm> detaillist = new observablecollection<store_detailvm>(); foreach (var item in shelvelist) { xdal.store_goods_detail.getgoodsdetail(item.shelve_code).tolist().foreach(obj => { detaillist.add(obj); }); } #region list<visualshelvevm> visuallist = new list<visualshelvevm>(); for (int i = 0; i < shelvelist.count; i++) { //循环最大粒子,在这个最大粒子当中来组织json for (int g = 0; g < 4; g++) { for (int l = 0; l < 4; l++) { var store_detail = detaillist.firstordefault(a => a.grid == g.tostring() && a.shelve_code == shelvelist[i].shelve_code && a.layer == l.tostring()); //如果未出库的里没有这个 那就可以添加 if (store_detail != null) { visuallist.add(new visualshelvevm() { shelve_grid = g.tostring(), shelve_layer = l.tostring(), shelve_code = shelvelist[i].shelve_code, shelve_name = shelvelist[i].shelve_name, ishas = true, goods_code = store_detail.goods_code, goods_name = store_detail.goods_name }); } else { visuallist.add(new visualshelvevm() { ishas = false, shelve_grid = g.tostring(), shelve_layer = l.tostring(), }); } } } } #endregion visualization.cefsha.executescriptasync($@"getshelve({newtonsoft.json.jsonconvert.serializeobject(visuallist)});"); }
需要注意的是该代码块的最后一行,这是我们用前端方法的方式,visualization是我们刚才的页面,我们直接调用静态cefsha然后执行它的异步方法,其中是直接执行的js,它和wpf自带的webbrowser还不一样,微软的webbrowser是高度封装的,而cefsha是高度开源的,这一块不得不赞了。
前台如何请求后台呢?首先在页面加载的时候配置下bound对象。
<script> window.onload = function () { loadscrollwidth(2); // 新式注入 cefsharp.bindobjectasync("bound"); } </script>
随后我们就可以通过bound这个对象来请求后台了
bound.getobjbysline_code(sline_code);
就以我来说,千万不要拿jquery和vue一起用,真的是太糟心了..
<body> <div class="container" id="app" style="max-width: 1600px;"> <div class="row"> <div class="col-lg-3"> <div class="card"> <h3 class="card-img-top headtext">清单</h3> <div class="card-body"> <table class="table table-striped"> <thead> <tr> <th scope="col">分类名称</th> <th scope="col">数量</th> </tr> </thead> <tbody> <tr v-for="(item,index) in goods_item"> <th scope="row">{{item.goods_name}}</th> <td>{{item.num}}</td> </tr> </tbody> </table> </div> </div> </div> <div class="col-lg-9" style="overflow: scroll;"> <div class="row line_div"> <div class="row" style="font-size: 20px;background-color: #ccc;width: 100%;"> <ul class="nav nav-pills nav-fill" id="nav_list"> <li class="nav-item" v-for="(item,index) in line_item"> <!-- 如果 index 1--> <a class="nav-link active" key="item.sline_code" @click="toggle(index,item)" v-if="index==0">{{item.sline_name}}</a> <!-- index 2 n --> <a class="nav-link" key="item.sline_code" @click="toggle(index,item)" v-if="index!=0">{{item.sline_name}}</a> </li> </ul> </div> <div class="row" style="margin-left: 0;" id="visualbody"> <div class="colums" v-for="item in reversedmessage"> <div class="row"> <img src="tx_3.png"> <div class="table_div"> <table class="custormtable" style="margin-top: 40px;" :id="item.code"> </table> </div> </div> <div class="row" style="background-image: url('tx_2.png');width: 556px;height:464px;background-repeat: repeat-y;"> </div> <div class="row"> <img src="tx_1.png"> <div class="hj_center"> <h3>{{item.name}}</h3> </div> </div> </div> </div> </div> </div> </div> </div> </body> <script> window.onload = function () { loadscrollwidth(2); // 新式注入 cefsharp.bindobjectasync("bound"); } </script> <script> function loadscrollwidth(num) { var tags = document.getelementsbyclassname('line_div')[0]; tags.style.width = num * 590 + 'px'; } function changedom(listdata,shelvedata) { var num = 1; $(".custormtable").html(""); shelvedata.shift(); for (var i = 0; i < shelvedata.length; i++) { var shelve_code = shelvedata[i].shelve_code; //不管如何都是循环4x4 for (var y = 0; y < 4; y++) { var goodshtml = '<tbody><tr class="store_goodsname">'; var numhtml = '<tr class="store_goodsid">' numhtml += "<td>" + num + "</td>"; numhtml += "<td>" + (num + 1) + "</td>"; numhtml += "<td>" + (num + 2) + "</td>"; numhtml += "<td>" + (num + 3) + "</td>"; numhtml = numhtml + '</tr>'; for (var g = 0; g < 4; g++) { var obj = listdata.find(item => item.shelve_layer == y && item.shelve_grid == g && item.shelve_code == shelve_code); if (obj != null) { if (obj.ishas == true) { goodshtml = goodshtml + "<td>" + obj.goods_name + "</td>"; } } else { goodshtml = goodshtml + "<td></td>"; } } goodshtml = goodshtml + '</tr>' + numhtml + '</tbody>'; var my_element = $("#" + shelve_code); $("#" + shelve_code).append(goodshtml); num = num + 4; } } } </script> </html> <script> var app = new vue({ el: "#app", data: { m: "hello", line_item: [],//列 goods_item: [],//商品列表+num shelve_list: [],//货架列表 listdata: [],//商品列表 },mounted() { //全局钩子引用 window.getalert = this.getalert; window.getgoods = this.getgoods; window.getshelve = this.getshelve; window.dbtwo = this.dbtwo; },methods: { getalert: function (list) { this.line_item = typeof list == 'string' ? json.parse(list) : list; },toggle:function(index,item){ $(".nav-item a").each(function () { $(this).removeclass("active"); }); $(".nav-item a").eq(index).addclass('active'); //item 对象 sline_code 【字段】 var sline_code = item.sline_code; //调用完之后c#会找我们的一个方法然后将参数带过来 bound.getshelvedetail(sline_code); //请求后台,让后台直接调用一个方法去加载列表。 bound.getobjbysline_code(sline_code); this.wecas(index, item); },wecas: function (index, item) { $(".nav-item a").each(function () { $(this).removeclass("active"); }); $(".nav-item a").eq(index).addclass('active'); //item 对象 sline_code 【字段】 var sline_code = item.sline_code; //调用完之后c#会找我们的一个方法然后将参数带过来 bound.getshelvedetail(sline_code); //请求后台,让后台直接调用一个方法去加载列表。 bound.getobjbysline_code(sline_code); },getgoods: function (goods_list) { //该方法是返回货架有的物品 this.goods_item = typeof goods_list == 'string' ? json.parse(goods_list) : goods_list; }, getshelve: function (list) { this.listdata = list; let obj = {}; list = list.reduce((cur, next) => { obj[next.shelve_code] ? "" : obj[next.shelve_code] = true && cur.push(next); return cur; }, []); this.shelve_list = typeof list == 'string' ? json.parse(list) : list; changedom(this.listdata, this.shelve_list); } }, computed: { //所有的货架列出来 reversedmessage: function () { var array = []; for(var i=0;i<this.shelve_list.length;i++){ //判断是否name是不是有 if (this.shelve_list[i].shelve_name != null) { var obj = { name: this.shelve_list[i].shelve_name, code : this.shelve_list[i].shelve_code } array.push(obj); } } if (array.length < 2) { loadscrollwidth(2); } else { loadscrollwidth(array.length); } return array; } } }); </script>
前后端交互最好是前端请求后端,然后让后端执行前端的js带上参数。
需要注意的cefsharp初次调用js的是 cefsha.executescriptasyncwhenpageloaded($"getalert({str});"); 我也是醉
还有就是在开发中一定想要f12 看看代码运行状况如何或者要调试。
class cefkeyboardhander : ikeyboardhandler { public bool onkeyevent(iwebbrowser browsercontrol, ibrowser browser, keytype type, int windowskeycode, int nativekeycode, cefeventflags modifiers, bool issystemkey) { if (type == keytype.keyup && enum.isdefined(typeof(keys), windowskeycode)) { var key = (keys)windowskeycode; switch (key) { case keys.f12: browser.showdevtools(); break; } } return false; } public bool onprekeyevent(iwebbrowser browsercontrol, ibrowser browser, keytype type, int windowskeycode, int nativekeycode, cefeventflags modifiers, bool issystemkey, ref bool iskeyboardshortcut) { return false; } }
ok就这样·~效果图
上一篇: angularjs2 ng2 密码隐藏显示的实例代码
下一篇: JS跳转手机站url的若干注意事项