欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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指向新页面):

CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接
<!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>
view code

请先大致看下winform版的cefsharp浏览器控件实现方式:

下面是wpf版的cefsharp浏览器控件的实现方式,与winform版相同的代码这里不再粘贴:

CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接
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

    }
}
view code

 核心代码(startnewwindow):

CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接
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);
    }
};
view code

核心代码(资源释放):

CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接
/// <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();
    }
}
view code

 难点:弹出窗口和原来的窗口似乎共用同一个chromiumwebbrowser实例的某些东西,但是弹出的窗口本身又获取不到chromiumwebbrowser实例,某些操作不便。