CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接
程序员文章站
2022-04-24 22:37:43
需求场景:在查询页面,填写查询条件,查询条件包括上传的图片,根据图片的特征查询,这就需要在提交的时候,使用POST提交,因为GET提交无法提交图片数据,提交查询条件之后,在新的窗口展示查询结果。(当然查询结果页面可能不支持F5刷新页面) 表单HTML代码示意(注意method="post" targ ......
需求场景:在查询页面,填写查询条件,查询条件包括上传的图片,根据图片的特征查询,这就需要在提交的时候,使用post提交,因为get提交无法提交图片数据,提交查询条件之后,在新的窗口展示查询结果。(当然查询结果页面可能不支持f5刷新页面)
表单html代码示意(注意method="post" target="_blank" action指向新页面):
<!doctype html> <html> <head> <title>提交表单查询</title> <script type="text/javascript" src='jquery.js'></script> <script type="text/javascript"> //保存 function save() { $("#frm").submit(); } </script> </head> <body> <form id="frm" action="searchresult" enctype="multipart/form-data" method="post" target="_blank"> <input type="text" class="input-text" id="title" name="title" value="测试title" style="width: 300px;" /> <input type="text" class="input-text" id="name" name="name" value="测试name" style="width: 300px;" /> <a href="javascript:void(0);" onclick="save()">保存</a> </form> </body> </html>
请先大致看下winform版的cefsharp浏览器控件实现方式:
下面是wpf版的cefsharp浏览器控件的实现方式,与winform版相同的代码这里不再粘贴:
using cefsharp; using log4net; using suncreate.vipf.base; using suncreate.vipf.client.bussiness; using system; using system.collections.generic; using system.componentmodel; 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; namespace suncreate.vipf.client.ui { /// <summary> /// 浏览器用户控件 /// </summary> public partial class browserctrl : usercontrol, idisposable { [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); private ilog _log = logmanager.getlogger(typeof(browserctrl)); 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; } /// <summary> /// 浏览器frameloadend事件 /// </summary> public event eventhandler frameloadend; private extchromiumbrowser _browser; public extchromiumbrowser browser { get { return this._browser; } } public browserctrl() { initializecomponent(); if (designerproperties.getisindesignmode(this)) return; this.loaded += browserctrl_loaded; lock (_lockobject) { if (!_iscefinited) { _iscefinited = true; initcef(true);//初始化cefsharp } } _browser = new extchromiumbrowser(); bindbrowser(_browser); grid.children.add(_browser); } /// <summary> /// 设置map控件接口,用于c#和js互操作 /// </summary> public void setmapctrl(imapctrl mapctrl) { _jsobject.mapctrl = mapctrl; } /// <summary> /// 释放资源 /// </summary> public void dispose() { //如果有弹出窗口则先释放它 foreach (uielement item in grid.children) { if (item is browsercontainer) { (item as browsercontainer).clearresource(); } } if (_browser != null && !_browser.isdisposed) { _browser.dispose(); } } private void browserctrl_loaded(object sender, routedeventargs e) { } private void loadurl() { if (_firstload) { _firstload = false; system.threading.tasks.task.factory.startnew(() => { thread.sleep(100); if (url == null && seturlevent != null) { try { seturlevent(this, null); } catch (exception ex) { _log.error("browserctrl loadurl error 获取url失败", ex); } } this.dispatcher.invoke(new action(() => { _browser.load(url); })); }); } } private void bindbrowser(extchromiumbrowser browser) { _jsobject = new jsobject(); browser.registerjsobject("jsobj", _jsobject, false); browser.startnewwindow += (s, e) => { try { intptr hwndchild = intptr.zero; //浏览器弹出窗口句柄 browsercontainer browsercontainer = new browsercontainer(); system.windows.forms.control control = new system.windows.forms.control(); control.dock = system.windows.forms.dockstyle.fill; control.createcontrol(); browsercontainer.host.child = control; browsercontainer.clearresourceevent += (ss, ee) => { closewindow(hwndchild); control.dispose(); browsercontainer.host.dispose(); }; //释放上一个弹出窗口 foreach (uielement item in grid.children) { if (item is browsercontainer) { (item as browsercontainer).clearresource(); } } grid.children.clear(); grid.children.add(browsercontainer); e.windowinfo.setaschild(control.handle, 0, 0, (int)browsercontainer.actualwidth, (int)browsercontainer.actualheight); browsercontainer.sizechanged += (ss, ee) => { hwndchild = findwindowex(control.handle, intptr.zero, null, null); movewindow(hwndchild, 0, 0, (int)browsercontainer.actualwidth, (int)browsercontainer.actualheight, true); }; } catch (exception ex) { _log.error("browserctrl bindbrowser error", ex); } }; 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.d) { //_browser.executescriptasync("dayornightmap", 0); } if (ee.key == key.n) { //_browser.executescriptasync("dayornightmap", 1); } }; browser.loaderror += (ss, ee) => { _log.error("extchromiumbrowser loaderror 错误码:" + ee.errorcode + ",错误信息:" + ee.errortext + ",错误url:" + ee.failedurl); return; //下面代码不执行 system.threading.tasks.task.factory.startnew(() => { if (url == null && seturlevent != null) { try { seturlevent(this, null); } catch (exception ex) { _log.error("browserctrl loadurl error 获取url失败", ex); } } thread.sleep(500); this.dispatcher.begininvoke(new action(() => { (ss as extchromiumbrowser).load(url); })); }); }; } #region 初始化cefsharp public static void initcef(bool multithreadedmessageloop) { string cefsharpfolder = "cefsharp.v49.0.1"; 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 = multithreadedmessageloop; settings.focusednodechangedenabled = true; cef.oncontextinitialized = delegate { var cookiemanager = cef.getglobalcookiemanager(); cookiemanager.setstoragepath(cefsharpfolder + "/cookies", true); cookiemanager.setsupportedschemes("custom"); }; settings.browsersubprocesspath = appdomain.currentdomain.basedirectory + cefsharpfolder + "/cefsharp.browsersubprocess.exe"; settings.logfile = cefsharpfolder + "/debug.log"; settings.cefcommandlineargs.add("disable-gpu", "1"); settings.cefcommandlineargs.add("enable-media-stream", "1"); if (!cef.initialize(settings, shutdownonprocessexit: true, performdependencycheck: true)) { throw new exception("unable to initialize cef"); } } #endregion } }
核心代码(startnewwindow):
browser.startnewwindow += (s, e) => { try { intptr hwndchild = intptr.zero; //浏览器弹出窗口句柄 browsercontainer browsercontainer = new browsercontainer(); system.windows.forms.control control = new system.windows.forms.control(); control.dock = system.windows.forms.dockstyle.fill; control.createcontrol(); browsercontainer.host.child = control; browsercontainer.clearresourceevent += (ss, ee) => { closewindow(hwndchild); control.dispose(); browsercontainer.host.dispose(); }; //释放上一个弹出窗口 foreach (uielement item in grid.children) { if (item is browsercontainer) { (item as browsercontainer).clearresource(); } } grid.children.clear(); grid.children.add(browsercontainer); e.windowinfo.setaschild(control.handle, 0, 0, (int)browsercontainer.actualwidth, (int)browsercontainer.actualheight); browsercontainer.sizechanged += (ss, ee) => { hwndchild = findwindowex(control.handle, intptr.zero, null, null); movewindow(hwndchild, 0, 0, (int)browsercontainer.actualwidth, (int)browsercontainer.actualheight, true); }; } catch (exception ex) { _log.error("browserctrl bindbrowser error", ex); } };
核心代码(资源释放):
/// <summary> /// 释放资源 /// </summary> public void dispose() { //如果有弹出窗口则先释放它 foreach (uielement item in grid.children) { if (item is browsercontainer) { (item as browsercontainer).clearresource(); } } if (_browser != null && !_browser.isdisposed) { _browser.dispose(); } }
难点:弹出窗口和原来的窗口似乎共用同一个chromiumwebbrowser实例的某些东西,但是弹出的窗口本身又获取不到chromiumwebbrowser实例,某些操作不便。
上一篇: 对CSS浮动,定位的简单理解