CefSharp禁止弹出新窗体,在同一窗口打开链接,或者在新Tab页打开链接,并且支持带type="POST" target="_blank"的链接
说明:在同一窗口打开链接,只要稍加改造就可以实现,这里实现的是在新tab页打开链接,并且支持带type="post" target="_blank"的链接
github和bitbucket上相关问题:
1、wpf empty post data when using custom popup https://github.com/cefsharp/cefsharp/issues/1267
2、ceflifespanhandler, customized onbeforepopup problem
解决(cefsharp版本75.1.143.0):
一、实现irequesthandler接口
using cefsharp; using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; using system.security.cryptography.x509certificates; namespace cefsharpdemo { public class requesthandler : irequesthandler { private extchromiumbrowser _browser; public requesthandler(extchromiumbrowser browser) { _browser = browser; } public bool getauthcredentials(iwebbrowser chromiumwebbrowser, ibrowser browser, string originurl, bool isproxy, string host, int port, string realm, string scheme, iauthcallback callback) { return false; } public iresourcerequesthandler getresourcerequesthandler(iwebbrowser chromiumwebbrowser, ibrowser browser, iframe frame, irequest request, bool isnavigation, bool isdownload, string requestinitiator, ref bool disabledefaulthandling) { if (request.method.toupper() == "post" && request.postdata != null) { if (request.postdata.elements.count > 0) { _browser.postdata = new byte[request.postdata.elements[0].bytes.length]; request.postdata.elements[0].bytes.copyto(_browser.postdata, 0); } } return null; } public bool onbeforebrowse(iwebbrowser chromiumwebbrowser, ibrowser browser, iframe frame, irequest request, bool usergesture, bool isredirect) { return false; } public bool oncertificateerror(iwebbrowser chromiumwebbrowser, ibrowser browser, ceferrorcode errorcode, string requesturl, isslinfo sslinfo, irequestcallback callback) { return false; } public bool onopenurlfromtab(iwebbrowser chromiumwebbrowser, ibrowser browser, iframe frame, string targeturl, windowopendisposition targetdisposition, bool usergesture) { return false; } public void onplugincrashed(iwebbrowser chromiumwebbrowser, ibrowser browser, string pluginpath) { } public bool onquotarequest(iwebbrowser chromiumwebbrowser, ibrowser browser, string originurl, long newsize, irequestcallback callback) { return false; } public void onrenderprocessterminated(iwebbrowser chromiumwebbrowser, ibrowser browser, cefterminationstatus status) { } public void onrenderviewready(iwebbrowser chromiumwebbrowser, ibrowser browser) { } public bool onselectclientcertificate(iwebbrowser chromiumwebbrowser, ibrowser browser, bool isproxy, string host, int port, x509certificate2collection certificates, iselectclientcertificatecallback callback) { return false; } } }
二、实现ilifespanhandler接口
using cefsharp; using system; using system.collections.generic; using system.collections.specialized; using system.linq; using system.runtime.interopservices; using system.text; using system.threading; using system.threading.tasks; using system.windows; using system.windows.interop; using utils; namespace cefsharpdemo { public class ceflifespanhandler : cefsharp.ilifespanhandler { private static limitedtaskscheduler _scheduler = new limitedtaskscheduler(2); public ceflifespanhandler() { } public bool doclose(iwebbrowser browsercontrol, cefsharp.ibrowser browser) { if (browser.isdisposed || browser.ispopup) { return false; } return true; } public void onaftercreated(iwebbrowser browsercontrol, ibrowser browser) { } public void onbeforeclose(iwebbrowser browsercontrol, ibrowser browser) { } public bool onbeforepopup(iwebbrowser browsercontrol, ibrowser browser, iframe frame, string targeturl, string targetframename, windowopendisposition targetdisposition, bool usergesture, ipopupfeatures popupfeatures, iwindowinfo windowinfo, ibrowsersettings browsersettings, ref bool nojavascriptaccess, out iwebbrowser newbrowser) { var chromiumwebbrowser = (extchromiumbrowser)browsercontrol; chromiumwebbrowser.dispatcher.invoke(new action(() => { browserpopupwin win = new browserpopupwin(); win.showintaskbar = false; win.height = 0; win.width = 0; win.show(); intptr handle = new windowinterophelper(win).handle; windowinfo.setaschild(handle); _scheduler.run(() => { waitutil.wait(() => chromiumwebbrowser.postdata); irequest request = null; if (chromiumwebbrowser.postdata != null) { request = frame.createrequest(); request.url = targeturl; request.method = "post"; request.initializepostdata(); var element = request.postdata.createpostdataelement(); element.bytes = chromiumwebbrowser.postdata; request.postdata.addelement(element); chromiumwebbrowser.postdata = null; } chromiumwebbrowser.dispatcher.invoke(new action(() => { newwindoweventargs e = new newwindoweventargs(targeturl, request); chromiumwebbrowser.onnewwindow(e); })); chromiumwebbrowser.dispatcher.invoke(new action(() => { win.close(); })); }); })); newbrowser = null; return false; } } }
三、扩展chromiumwebbrowser
using cefsharp.wpf; using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; namespace cefsharpdemo { public class extchromiumbrowser : chromiumwebbrowser { public byte[] postdata { get; set; } public extchromiumbrowser() : base() { this.lifespanhandler = new ceflifespanhandler(); this.downloadhandler = new downloadhandler(this); this.menuhandler = new menuhandler(); this.keyboardhandler = new keyboardhandler(); this.requesthandler = new requesthandler(this); } public event eventhandler<newwindoweventargs> startnewwindow; public void onnewwindow(newwindoweventargs e) { if (startnewwindow != null) { startnewwindow(this, e); } } public void clearhandlers() { //如果不清理handler,会导致子进程cefsharp.browsersubprocess.exe无法释放 this.lifespanhandler = null; this.downloadhandler = null; this.menuhandler = null; this.keyboardhandler = null; } } }
四、封装extchromiumbrowser(browserctrl控件)
using cefsharp; using cefsharp.wpf; using system; using system.collections.generic; using system.componentmodel; using system.io; using system.linq; using system.runtime.interopservices; using system.text; using system.threading; using system.threading.tasks; using system.windows; using system.windows.controls; using system.windows.data; using system.windows.documents; using system.windows.input; using system.windows.interop; using system.windows.media; using system.windows.media.imaging; using system.windows.navigation; using system.windows.shapes; using utils; namespace cefsharpdemo { /// <summary> /// 浏览器用户控件 /// </summary> public partial class browserctrl : usercontrol, idisposable { #region 外部方法 /* [dllimport("user32.dll", setlasterror = true)] private static extern intptr setparent(intptr hwndchild, intptr hwndnewparent); [dllimport("user32.dll", setlasterror = true)] public static extern intptr findwindowex(intptr parenthandle, intptr childafter, string classname, string windowtitle); [dllimport("user32.dll", setlasterror = true)] public static extern int movewindow(intptr hwnd, int x, int y, int nwidth, int nheight, bool brepaint); [dllimport("user32.dll", setlasterror = true)] public static extern int closewindow(intptr hwnd); [dllimport("user32.dll", entrypoint = "getwindowtext")] private static extern int getwindowtext(intptr hwnd, stringbuilder lpstring, int nmaxcount); */ #endregion #region 变量属性事件 private static bool _iscefinited = false; private static object _lockobject = new object(); private jsobject _jsobject; private bool _firstload = true; /// <summary> /// 在此事件中设置url(此事件已在线程中执行,此事件已对错误情况进行处理) /// </summary> public event eventhandler seturlevent; /// <summary> /// url /// </summary> public string url { get; set; } public irequest request { get; set; } /// <summary> /// 浏览器frameloadend事件 /// </summary> public event eventhandler frameloadend; private extchromiumbrowser _browser; public extchromiumbrowser browser { get { waitutil.wait(() => this._browser != null && this._browser.isinitialized && _iscefinited); return this._browser; } } private static limitedtaskscheduler _scheduler = new limitedtaskscheduler(2); #endregion #region 构造函数 public browserctrl() { initializecomponent(); if (designerproperties.getisindesignmode(this)) return; this.loaded += browserctrl_loaded; lock (_lockobject) { if (!_iscefinited) { _iscefinited = true; initcef();//初始化cefsharp } } _browser = new extchromiumbrowser(); bindbrowser(_browser); grid.children.add(_browser); } #endregion #region browserctrl_loaded private void browserctrl_loaded(object sender, routedeventargs e) { } #endregion #region setmapctrl /// <summary> /// 设置map控件接口,用于c#和js互操作 /// </summary> public void setmapctrl(imapctrl mapctrl) { _jsobject.mapctrl = mapctrl; } #endregion #region dispose 释放资源 /// <summary> /// 释放资源 /// </summary> public void dispose() { //如果有弹出窗口则先释放它 //foreach (uielement item in grid.children) //{ // if (item is browsercontainer) // { // (item as browsercontainer).clearresource(); // } //} _browser.clearhandlers(); if (_browser != null && !_browser.isdisposed) { _browser.dispose(); } } #endregion #region load public void load(string url) { if (!string.isnullorwhitespace(url)) { loadingwait.visibility = visibility.visible; url = url; _scheduler.run(() => { #region wait waitutil.wait(() => { if (this._browser == null) return false; if (!this._browser.isinitialized) return false; if (!_iscefinited) return false; bool isbrowserinitialized = false; this.dispatcher.invoke(() => { isbrowserinitialized = this._browser.isbrowserinitialized; }); if (!isbrowserinitialized) return false; return true; }); #endregion _browser.load(url); }); } } #endregion #region loadurl private void loadurl() { if (_firstload) { _firstload = false; _scheduler.run(() => { #region wait waitutil.wait(() => { if (this._browser == null) return false; if (!this._browser.isinitialized) return false; if (!_iscefinited) return false; bool isbrowserinitialized = false; this.dispatcher.invoke(() => { isbrowserinitialized = this._browser.isbrowserinitialized; }); if (!isbrowserinitialized) return false; return true; }); #endregion if (url == null && seturlevent != null) { try { seturlevent(this, null); } catch (exception ex) { logutil.error(ex, "browserctrl loadurl error 获取url失败"); } } else { this.dispatcher.invoke(new action(() => { loadingwait.visibility = visibility.collapsed; })); } if (url != null) { try { if (request == null) { _browser.load(url); } else { _browser.load(url); _browser.getmainframe().loadrequest(request); request = null; } } catch (exception ex) { logutil.error(ex, "browserctrl loadurl error load url失败"); } } else { this.dispatcher.invoke(new action(() => { loadingwait.visibility = visibility.collapsed; })); } }); } } #endregion #region bindbrowser private void bindbrowser(extchromiumbrowser browser) { _jsobject = new jsobject(); browser.registerjsobject("jsobj", _jsobject, new cefsharp.bindingoptions { camelcasejavascriptnames = false }); browser.isbrowserinitializedchanged += (ss, ee) => { loadurl(); }; browser.frameloadstart += (ss, ee) => { this.dispatcher.begininvoke(new action(() => { (ss as extchromiumbrowser).focus(); })); }; browser.frameloadend += (ss, ee) => { this.dispatcher.begininvoke(new action(() => { loadingwait.visibility = visibility.collapsed; })); if (frameloadend != null) { frameloadend(null, null); } }; browser.keydown += (ss, ee) => { if (ee.key == key.f5) { try { browser.reload(); } catch (exception ex) { logutil.error(ex, "extchromiumbrowser reload error"); } } }; browser.previewtextinput += (o, e) => { foreach (var character in e.text) { // 把每个字符向浏览器组件发送一遍 browser.getbrowser().gethost().sendkeyevent((int)wm.char, (int)character, 0); } // 不让cef自己处理 e.handled = true; }; browser.loaderror += (s, e) => { this.dispatcher.begininvoke(new action(() => { loadingwait.visibility = visibility.collapsed; })); }; } #endregion #region registerjsobject public void registerjsobject(string name, object objecttobind, bindingoptions options = null) { try { if (_browser != null) { _browser.registerjsobject(name, objecttobind, options); } } catch (exception ex) { logutil.error(ex, "browserctrl registerjsobject 错误"); } } #endregion #region 初始化cefsharp public static void initcef() { string cefsharpfolder = "cefsharp"; var settings = new cefsettings(); //the location where cache data will be stored on disk. if empty an in-memory cache will be used for some features and a temporary disk cache for others. //html5 databases such as localstorage will only persist across sessions if a cache path is specified. settings.cachepath = cefsharpfolder + "/cache"; //设置cache目录 settings.multithreadedmessageloop = true; cefsharpsettings.focusednodechangedenabled = true; cefsharpsettings.legacyjavascriptbindingenabled = true; cefsharpsettings.shutdownonexit = true; cefsharpsettings.subprocessexitifparentprocessclosed = true; string logdir = appdomain.currentdomain.basedirectory + cefsharpfolder + "/log/"; if (!directory.exists(logdir)) { directory.createdirectory(logdir); } settings.browsersubprocesspath = appdomain.currentdomain.basedirectory + cefsharpfolder + "/cefsharp.browsersubprocess.exe"; settings.logfile = logdir + datetime.now.tostring("yyyymmdd") + ".log"; settings.localesdirpath = appdomain.currentdomain.basedirectory + cefsharpfolder + "/locales"; settings.cefcommandlineargs.add("disable-gpu", "1"); settings.cefcommandlineargs.add("enable-media-stream", "1"); if (!cef.initialize(settings, performdependencycheck: true, browserprocesshandler: new browserprocesshandler())) { throw new exception("unable to initialize cef"); } } #endregion } }
五、mainwindow测试代码
using cefsharp; using system; using system.collections.generic; using system.io; using system.linq; using system.text; using system.threading.tasks; using system.windows; using system.windows.controls; using system.windows.data; using system.windows.documents; using system.windows.input; using system.windows.media; using system.windows.media.imaging; using system.windows.navigation; using system.windows.shapes; using utils; namespace cefsharpdemo { /// <summary> /// cefsharp demo 窗体 /// </summary> public partial class mainwindow : window { public mainwindow() { initializecomponent(); tabcontrol.addtabitemevent += tabcontrol_addtabitemevent; application.current.mainwindow = this; } private void tabcontrol_addtabitemevent(object sender, eventargs e) { //createtabitem("https://www.cnblogs.com/"); createtabitem("file:///d:/_程序/cefsharpdemo/post.html"); } /// <summary> /// 新增tab页 /// </summary> private void createtabitem(string url = null, irequest request = null) { tabitem tabitem = new tabitem(); tabitem.header = "新标签页"; browserdemoctrl ctrl = new browserdemoctrl(); ctrl.browserctrl.browser.startnewwindow += (s, e) => { createtabitem(e.targeturl, e.request); }; ctrl.browserctrl.seturlevent += (s, e) => { ctrl.browserctrl.url = url; ctrl.browserctrl.request = request; }; tabitem.content = ctrl; tabcontrol.items.add(tabitem); tabcontrol.selecteditem = tabitem; scrollviewer scrollviewer = tabcontrol.template.findname("scrollviewer", tabcontrol) as scrollviewer; scrollviewer.scrolltorightend(); } private void window_closed(object sender, eventargs e) { tabcontrol.closealltabitem(); //关闭窗体清理资源 //程序退出时删除cache cefsharp.cef.shutdown(); string cachepath = appdomain.currentdomain.basedirectory + "cefsharp\\cache"; if (directory.exists(cachepath)) { foreach (string path in directory.getdirectories(cachepath)) { directory.delete(path, true); } foreach (string file in directory.getfiles(cachepath)) { if (!file.tolower().contains("cookies")) { file.delete(file); } } } } } }
六、测试html代码post.html
<!doctype html> <html> <head> <title>cefsharpdemo</title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> </style> <script type="text/javascript"> </script> </head> <body> <!--enctype="multipart/form-data"--> <form method="post" action="http://localhost:1209/netcms/" target="_blank"> <span>name:</span><input type="text" name="name" value="测试名称" /> <span>code:</span><input type="text" name="code" value="测试编码" /> <button type="submit">post提交</button> </form> </body> </html>
七、测试后台代码
public actionresult index() { string name = request.params["name"]; string code = request.params["code"]; viewbag.name = name; viewbag.code = code; return view(); }
八、测试前台cshtml代码
@using models; @{ layout = "~/views/shared/_sitelayout.cshtml"; } <div style="font-size:50px; height:1200px;"> <span>name:</span><span>@viewbag.name</span><br /><span>code:</span><span>@viewbag.code</span> </div>
九:关键代码段:
1、requesthandler类中获取并保存postdata
public iresourcerequesthandler getresourcerequesthandler(iwebbrowser chromiumwebbrowser, ibrowser browser, iframe frame, irequest request, bool isnavigation, bool isdownload, string requestinitiator, ref bool disabledefaulthandling) { if (request.method.toupper() == "post" && request.postdata != null) { if (request.postdata.elements.count > 0) { _browser.postdata = new byte[request.postdata.elements[0].bytes.length]; request.postdata.elements[0].bytes.copyto(_browser.postdata, 0); } } return null; }
2、ceflifespanhandler类中创建irequest
public bool onbeforepopup(iwebbrowser browsercontrol, ibrowser browser, iframe frame, string targeturl, string targetframename, windowopendisposition targetdisposition, bool usergesture, ipopupfeatures popupfeatures, iwindowinfo windowinfo, ibrowsersettings browsersettings, ref bool nojavascriptaccess, out iwebbrowser newbrowser) { var chromiumwebbrowser = (extchromiumbrowser)browsercontrol; chromiumwebbrowser.dispatcher.invoke(new action(() => { browserpopupwin win = new browserpopupwin(); win.showintaskbar = false; win.height = 0; win.width = 0; win.show(); intptr handle = new windowinterophelper(win).handle; windowinfo.setaschild(handle); _scheduler.run(() => { waitutil.wait(() => chromiumwebbrowser.postdata); irequest request = null; if (chromiumwebbrowser.postdata != null) { request = frame.createrequest(); request.url = targeturl; request.method = "post"; request.initializepostdata(); var element = request.postdata.createpostdataelement(); element.bytes = chromiumwebbrowser.postdata; request.postdata.addelement(element); chromiumwebbrowser.postdata = null; } chromiumwebbrowser.dispatcher.invoke(new action(() => { newwindoweventargs e = new newwindoweventargs(targeturl, request); chromiumwebbrowser.onnewwindow(e); })); chromiumwebbrowser.dispatcher.invoke(new action(() => { win.close(); })); }); })); newbrowser = null; return false; }
说明:onbeforepopup方法要return false,用browserpopupwin和windowinfo.setaschild方法弹出一个不可见的窗体,这样才能拿到postdata
3、在browserctrl控件中用loadrequest方法打开新的url,并把post数据带过去
if (request == null) { _browser.load(url); } else { _browser.load(url); _browser.getmainframe().loadrequest(request); request = null; }
十、效果图:
完整代码下载:https://files-cdn.cnblogs.com/files/s0611163/cefsharpdemo.zip
源码说明:为了减少源码压缩包的大小,代码中没有依赖的cefsharp文件,请自己下载(使用x86版本),用于测试的网页后台代码也没有,请自己制作测试后台
上一篇: 春天早餐吃什么最好,你真的吃对了吗
下一篇: 火遍大江南北的麦片牛奶减肥,你值得有用