基于.net开发chrome核心浏览器【四】

55 篇文章 0 订阅
11 篇文章 0 订阅

基于.net开发chrome核心浏览器【一】

基于.net开发chrome核心浏览器【二】

基于.net开发chrome核心浏览器【三】

基于.net开发chrome核心浏览器【四】

基于.net开发chrome核心浏览器【五】

基于.net开发chrome核心浏览器【六】

基于.net开发chrome核心浏览器【七】


一:



上周去北京出差,给国家电网的项目做架构方案,每天都很晚睡,客户那边的副总也这样拼命工作。


累的不行了,直接导致第四篇文章没有按时发出来。


希望虚心学习1,小侠客等关注我的朋友们原谅我。


二:


在这篇文章中,我们主要实现下面三个功能:


浏览器地址栏、浏览器窗口大小变化、浏览器下载文件


为了实现这三个功能,我们新创建了一个工程,


program.cs文件里的内容没有任何变动;


dll文件夹里的内容没有任何变动;


资源的引用,程序集的配置,都没有做任何变动;


三:


我们在解决方案中创建一个bs文件夹,这个文件夹中放置与浏览器相关的类。


首先在这个文件夹中创建一个叫BsDownloadHandler的类


顾名思义,这个类是为下载文件而创建的。


如果想实现下载文件,首先要让这个类继承CefDownloadHandler


然后重写父类的OnBeforeDownload和OnDownloadUpdated两个方法


重写的代码如下:


复制代码
        protected override void OnBeforeDownload(CefBrowser browser, CefDownloadItem downloadItem, string suggestedName, CefBeforeDownloadCallback callback)
        {
            callback.Continue(string.Empty, true);
        }
        protected override void OnDownloadUpdated(CefBrowser browser, CefDownloadItem downloadItem, CefDownloadItemCallback callback)
        {
            if (downloadItem.IsComplete)
            {
                MessageBox.Show("下载成功");
                if (browser.IsPopup && !browser.HasDocument)
                {
                    browser.GetHost().ParentWindowWillClose();
                    browser.GetHost().CloseBrowser();
                }
            }
        }
复制代码
OnBeforeDownload方法,在浏览器开始下载文件之前被调用,CEF在默认情况下屏蔽了所有文件下载的事件


如果希望CEF处理下载事件,那么就要调用callback参数的Continue事件。


suggested_name参数是下载文件的建议名称,也就是保存文件对话框出来之后,文件名称文本框里的内容。


--------------------------


OnDownloadUpdated方法,CEF在下载文件过程中会多次调用此方法,并不是只有完成下载了之后再调用这个方法。


如果你想终止下载,也可以调用callback参数的Cancel方法


--------------------------


downloadItem是描述下载文件的类型的实例,


他里面有一系列的属性,


包括:是否完成、是否取消、当前速度、完成的百分比、完成了多少比特、一共有多少比特、开始时间、结束时间等等


不要试图在上面两个方法之外,引用这个实例。


----------------------------


因为浏览器下载文件有很多方式,有可能是通过window.open(js)的方式打开一个路径来下载文件


这时,我们要关掉被打开的窗口,(CEF不会自动帮我们关掉这类窗口)所以在获知下载完成之后,又执行了如下两句代码:


                if (browser.IsPopup && !browser.HasDocument)
                {
                    browser.GetHost().ParentWindowWillClose();
                    browser.GetHost().CloseBrowser();
                }
四:


为了能使浏览器的窗口大小,随着容器的窗口大小变化而变化。


我们必须要知道,浏览器的窗口句柄何时创建成功,何时被加入到父窗口中去了。


在上一篇文章中,我们提到CefBrowserHost.CreateBrowser方法是异步的。


我们要想一些办法,来获取这个方法执行成功后,所创建的浏览器窗口的句柄。


首先,我们要在bs文件夹内创建一个名为BsLifeSpanHandler的类


这个类继承自CefLifeSpanHandler,我们在这个类中重写了OnAfterCreated方法


整个类的代码如下:


复制代码
    public class BsLifeSpanHandler : CefLifeSpanHandler
    {
        private BsClient bClient;
        public BsLifeSpanHandler(BsClient bc)
        {
            bClient = bc;
        }
        protected override void OnAfterCreated(Xilium.CefGlue.CefBrowser browser)
        {
            base.OnAfterCreated(browser);
            bClient.Created(browser);
        }
    }
复制代码
创建这个类的实例,必须传入一个BsClient的实例,关于BsClient的内容,我们稍后再讲


基类CefLifeSpanHandler中有很多方法可供重载。


包括:弹窗之前的事件、浏览器窗口创建成功后的事件、执行模态窗的事件、关闭窗口之前的事件


(虽然这里叫事件,但其实是方法,只不过CEF会自动调用这些方法)


我们在这个类中重写了OnAfterCreated方法(浏览器窗口创建成功后的事件),


在这个方法中,我们调用了BsClient实例的Created方法,


并且把browser实例当作参数传递给了这个方法


这里的browser其实就是我们创建出来的浏览器核心,可以通过它获取浏览器的窗口句柄。


五: 


我们在上一篇文章中,创建了一个BrowserClient类,这个类继承自CefClient。


但我们并没有给这个类任何实现,只是在调用CefBrowserHost.CreateBrowser方法时,传递了这个类的一个实例


现在,我们把这个类放到bs文件夹中去,并改名为BsClient,为这个类增加如下实现代码:


复制代码
    public class BsClient:CefClient
    {
        public event EventHandler OnCreated;
        private readonly CefLifeSpanHandler lifeSpanHandler;
        private readonly CefDownloadHandler downloadHandler;        
        public BsClient()
        {
            lifeSpanHandler = new BsLifeSpanHandler(this);
            downloadHandler = new BsDownloadHandler();
        }
        protected override CefLifeSpanHandler GetLifeSpanHandler()
        {
            return lifeSpanHandler;
        }
        protected override CefDownloadHandler GetDownloadHandler()
        {
            return downloadHandler;
        }
        public void Created(CefBrowser bs)
        {
            if (OnCreated != null)
            {
                OnCreated(bs, EventArgs.Empty);
            }
        }
    }
复制代码
在这个类中,我们重写了GetLifeSpanHandler和GetDownloadHandler方法,


这样,我们前面创建的BsLifeSpanHandler和BsDownloadHandler才会物尽其用。


还可以在这里重写很多方法,


比如:GetContextMenuHandler(右键菜单句柄),GetDialogHandler(对话框句柄)GetKeyboardHandler(键盘句柄)等


在这个类中我们还公开了一个OnCreated的事件,Created方法会调用这个事件。


而这个方法,是在BsLifeSpanHandler类中被调用的。


也就是说,当浏览器创建完成时,OnCreated事件会被触发。


六:


我们在bs文件夹下创建一个类BsCtl


这个类就是我们的浏览器控件,


先来看一下这个类唯一的构造函数:


复制代码
        public BsCtl(Control ctl)
        {
            parent = ctl;
            var cwi = CefWindowInfo.Create();
            cwi.SetAsChild(parent.Handle, new CefRectangle(0, 0, parent.Width, parent.Height));
            var bc = new BsClient();
            bc.OnCreated += bc_OnCreated;
            var bs = new CefBrowserSettings() { };
            CefBrowserHost.CreateBrowser(cwi, bc, bs, "http://www.cnblogs.com/liulun");
            parent.SizeChanged += parent_SizeChanged;
        }
复制代码
你会发现,上一篇文章中的几行核心代码,都搬到这里来了。


构造函数的参数ctl,是一个windows控件,一般是个panel之类的容器控件,


我们创建的浏览器窗口就将呈现在这个容器控件内


同时,我们为这个容器控件注册了SizeChanged事件


也为BsClient注册了OnCreated事件。


我们在创建默认浏览器的时候,指定了它的默认主页(就是我的博客)


你如果不想这么办,你可以指定它为:about:blank


----------------------------


下面我们看一下这两个事件的实现代码


复制代码
void bc_OnCreated(object sender, EventArgs e)
        {
            bs = (CefBrowser)sender;
            var handle = bs.GetHost().GetWindowHandle();
            ResizeWindow(handle,parent.Width,parent.Height);
        }


        void parent_SizeChanged(object sender, EventArgs e)
        {
            if (bs != null)
            {
                var handle = bs.GetHost().GetWindowHandle();
                ResizeWindow(handle, parent.Width, parent.Height);                
            }
        }
复制代码
在浏览器创建成功的事件中,我们把浏览器的实例保存成了私有属性


他是一个核心对象,以后有很多地方会用到。


我们通过bs.GetHost().GetWindowHandle();来获得浏览器窗口的句柄。


有了这个句柄,我们就可以重置浏览器窗口的大小,使他随着主窗体的大小变化而变化


--------------------------------


下面来看一下ResizeWindow方法的代码:


复制代码
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);


        private void ResizeWindow(IntPtr handle, int width, int height)
        {
            if (handle != IntPtr.Zero)
            {
                NativeMethod.SetWindowPos(handle, IntPtr.Zero,
                    0, 0, width, height,
                    0x0002 | 0x0004
                    );
            }
        }
复制代码
在ResizeWindow方法中,通过PInvoke方式调用了windows api的方法,来设置浏览器窗口的位置和大小


其中0x0002相当于SWP_NOMOVE;0x0004相当于SWP_NOZORDER


---------------------------


在这个类中,还有一个方法,代码如下:


        public void LoadUrl(string url)
        {
            bs.GetMainFrame().LoadUrl(url);
        }
这个方法负责切换浏览器的地址


七:


现在我们来设计主窗口




一些必要的布局代码如下,就不多做解释了:

 this.AddressContainer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 

 this.GoBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));

 this.AddressTB.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 

 this.BrowserContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
 this.StatusContainer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 


------------------------------------------


经过前面的一系列工作,我们主窗口的代码就精简了很多
public partial class Demo : Form
    {
        BsCtl bs;
        public Demo()
        {
            InitializeComponent();
            this.Name = "CefBrowser";
            this.Text = "最简单的实现";            
        }
        private void CefBrowser_Load(object sender, EventArgs e)
        {
            bs = new BsCtl(BrowserContainer);
        }


        private void GoBtn_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(AddressTB.Text))
            {
                bs.LoadUrl(AddressTB.Text);
            }
        }
    }


在窗口加载成功后,我们创建了浏览器的实例(创建时把浏览器容器传递给了构造函数),


当点击GO按钮的时候,切换了浏览器的地址。


-------------------------------------------------


最终的效果如下(浏览器窗口的大小会随着主窗口的大小而变化)


源码下载:

http://files.cnblogs.com/liulun/CefDemo2.zip


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值