基于Chromium构建Chrome WebBrowser for .net 控件(还有点心得体会)
http://blog.csdn.net/lllllllllluoyi/article/details/8540054
首先向360说句sorry,在2011年360极速浏览器出现的时候我去他们论坛里骂过。为什么要到歉呢,因为2012年我把我们公司使用IE WebBrowser改为Chrome控件了,中间遇到的辛酸使我明白360公司能做成产品确实不容易。言归正转,公司的壳程序是C#编写的WinForm程序,刚开始我只找到delphi的Chromium项目,然后在delphi2010中安装好控件后就生成DLL让WinForm程序调用,这种策略是我做这个控件的最大失败。因为我对delphi比较熟,很快控件就能在WinForm程序里跑起来了,很开心,立马根据壳中已经使用的事件和方法在自定义控件中实现,自测一下没有问题后就交给测试人员来试用,半天的功夫,测试员小张就来对我说:罗兄,网页中不能使用键盘上Tab键切换,回车事件响应不了......不会吧,是不是焦点没有定位到自定义控件中,我第一时间就是这样想,然后就是反复的折腾,还是不行。最后Google了一下,好不容易找到一个让自己信服的答案,就是win32的消息循环机制与.net消息机制不一样,嵌入到WinForm中的VCL控件不能得到消息。我靠,这还得了,leader还不把我劈了,leader已经吩咐美工全按chrome浏览器的样式来写了。兵来将挡,水来土淹,我火速Google一个能在.net上跑的版本“CefSharp” ,下载来看,傻眼了,是C++版的,咋办?熬夜啃吧!
我分为了两个项目,一是libfuncs,为了使DLL名称一致,我重命名了CefSharp项目,它负责提供操作浏览器的方法和触发事件;二是cwber,它是自定义的WinForm控件,用于在Form上的布局,必须引用libfuncs.dll。
源码地址:https://sourceforge.net/projects/chromewebbrowse
cwber比较简单,以下是它的代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using libfuncs; namespace cwber { public partial class ChromeWebBrowser : UserControl { private ChromeApp chrome = null; ToolTip myToolTip = new ToolTip(); public ChromeWebBrowser() { InitializeComponent(); ChromeWebBrowser.CheckForIllegalCrossThreadCalls = false; } private void ChromeWebBrowser_Load(object sender, EventArgs e) { if (chrome == null) { chrome = new ChromeApp(); } Start(); } #region 外部调用方法 libfuncs.DocumentCompletedEvent elementEvent1; libfuncs.FrameLoadStartEvent elementEvent2; libfuncs.FrameLoadEndEvent elementEvent3; libfuncs.FileDownloadingEvent fileDownloading; libfuncs.FileDownloadCompletedEvent fileDownloaded; libfuncs.ComponentInitialized componentInitialized; libfuncs.ToolTipEventListener toolTipEvent; public void Start() { if (chrome != null) { chrome.Dock = DockStyle.Fill; if (componentInitialized == null) { componentInitialized = new libfuncs.ComponentInitialized(componentInitializedEvent); chrome.RegisterEvent(componentInitialized); } Controls.Add(chrome); if (elementEvent1 == null) { elementEvent1 = new libfuncs.DocumentCompletedEvent(documentComplete); chrome.RegisterEvent(elementEvent1); } if (elementEvent2 == null) { elementEvent2 = new libfuncs.FrameLoadStartEvent(frameStartStart); chrome.RegisterEvent(elementEvent2); } if (elementEvent3 == null) { elementEvent3 = new libfuncs.FrameLoadEndEvent(frameLoadEnd); chrome.RegisterEvent(elementEvent3); } if (fileDownloading == null) { fileDownloading = new libfuncs.FileDownloadingEvent(this.downloading); chrome.RegisterEvent(fileDownloading); } if (fileDownloaded == null) { fileDownloaded = new libfuncs.FileDownloadCompletedEvent(this.downloaded); chrome.RegisterEvent(fileDownloaded); } if (toolTipEvent == null) { toolTipEvent = new libfuncs.ToolTipEventListener(this.ShowToolTipText); chrome.RegisterEvent(toolTipEvent); } chrome.Visible = true; chrome.BringToFront(); } } /* * 描述:释放浏览器 */ public void Free() { elementEvent1 = null; elementEvent2 = null; elementEvent3 = null; fileDownloading = null; fileDownloaded = null; componentInitialized = null; toolTipEvent = null; chrome.Dispose(); chrome = null; } /* * 参数:Url 打开网页地址 * 描述:打开网址。 */ public void OpenUrl(string Url) { if (chrome != null) chrome.Load(Url); } /* * 参数:id 网页中的控件元素ID * 描述:根据元素ID获取元素的值,适用于Input,A标签元素 */ public string GetElementValueById(string id) { return chrome == null ? "" : chrome.GetElementValueById(id); } /* * 参数:id 网页中的控件元素ID, value 元素新值 * 描述:为页面中元素赋予新值。 */ public void SetElementValueById(string id, string value) { if (chrome != null) { chrome.SetElementValueById(id, value); } } public delegate void TCallBackElementEventListener(); private List<libfuncs.ElementEventListener> elementEventList = new List<libfuncs.ElementEventListener>(); /* * 描述:附加元素的侦听事件。当该元素触发附加事件时,则执行TCallBackElementEventListener委托方法 */ public void AppendElementEventListener(string id, string eventName, TCallBackElementEventListener callFunc) { libfuncs.ElementEventListener elementEvent = new libfuncs.ElementEventListener(callFunc); elementEventList.Add(elementEvent); chrome.AddElementEventListener(id, eventName, elementEvent); } /* * 描述:向页面中注入并执行脚本。 */ public void ExecuteScript(string script) { if (chrome != null) chrome.ExecuteScript(script); } public object EvaluateScript(string script) { if (chrome != null) return chrome.EvaluateScript(script); else return null; } /* * 描述:计算文件单位。用于文件下载。 */ private string CompareFileSize(Int64 size) { //计算K,M单位 string strTotalSize = string.Empty; if (size < 1024) { strTotalSize = size.ToString() + " B"; } else if (size >= 1024 && size < 1024 * 1024) { strTotalSize = (size / 1024).ToString() + " KB"; } else { strTotalSize = (size / 1024 / 1024).ToString() + " MB"; } return strTotalSize; } #endregion #region 属性 public string Url { get { return chrome == null?"":chrome.Core.Address; } } #endregion #region 事件 /*控件初始化事件*/ public event EventHandler ComponentInitializedEventHandler; private void componentInitializedEvent() { EventArgs e = new EventArgs(); if (ComponentInitializedEventHandler != null) ComponentInitializedEventHandler(this, e); } /*页面加载完成事件*/ public event EventHandler DocumentCompletedEventHandler; private void documentComplete() { EventArgs e = new EventArgs(); if (DocumentCompletedEventHandler != null) DocumentCompletedEventHandler(this, e); } /*Frame加载完成事件,这里的Frame可以是页面本身,也是iFrame元素*/ public event EventHandler PageLoadFinishEventHandler; private void frameLoadEnd() { EventArgs e = new EventArgs(); if (PageLoadFinishEventHandler != null) PageLoadFinishEventHandler(this, e); } /*Frame加载开始事件,这里的Frame可以是页面本身,也是iFrame元素*/ public event EventHandler PageLoadStartEventHandler; private void frameStartStart() { EventArgs e = new EventArgs(); if (PageLoadStartEventHandler != null) PageLoadStartEventHandler(this, e); } /*下载中事件,不开放该事件*/ Form downloadForm = null; private void downloading(Int64 totalSize, Int64 loadedSize) { string strTotalSize = CompareFileSize(totalSize); string strLoadedSize = CompareFileSize(loadedSize); if (downloadForm == null) { downloadForm = new Form(); downloadForm.Text = "下载中"; downloadForm.Width = 280; downloadForm.Height = 150; downloadForm.MaximizeBox = false; downloadForm.MinimizeBox = false; downloadForm.ControlBox = false; downloadForm.StartPosition = FormStartPosition.CenterScreen; Label label = new Label(); label.Left = 20; label.Top = 50; label.Width = 250; label.Text = "已下载:" + strLoadedSize + "/" + strTotalSize; downloadForm.Controls.Add(label); } downloadForm.Show(); downloadForm.BringToFront(); foreach (Control c in downloadForm.Controls) { if (c is Label) { Label label = (Label)c; label.Text = "已下载:" + strLoadedSize + "/" + strTotalSize; label.Update(); } } downloadForm.Update(); } /*下载完成事件,不开放该事件*/ private void downloaded() { if (downloadForm != null) downloadForm.Close(); downloadForm = null; } /*消息提示事件,不开放*/ private void ShowToolTipText(string text) { if (chrome == null) return; if (string.IsNullOrEmpty(text)) { myToolTip.RemoveAll(); return; } //保证每行40个字 int len = text.Length; int offset = 40; int count = len / offset; for (int i = 1; i <= count; i++) { text = text.Insert(offset * i, "\n"); } myToolTip.ShowAlways = false; myToolTip.UseAnimation = true; myToolTip.UseFading = true; //t.SetToolTip(button1, text); Point p = Control.MousePosition; Point p1 = this.PointToClient(p); myToolTip.Show(text, chrome, p1.X+20, p1.Y+10); } #endregion } }
重点是libfuncs中的libfuncs.h、ChromeApp.h、ChromeApp.cpp、ClientAdapter.h、ClientAdapter.cpp五个文件,实现方法都在里面,其他的文件基本都是接口文件。我自己只根据项目实际需求来做的功能,接口没有全部实现。这个部分大家看源代码吧,我用到的地方都注释了。文采不行,写不动,大家原谅。睡了。
最后说句,我们公司网页美工解脱了!