基于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 < )
{
strTotalSize = size.ToString() + " B";
}
else if (size >= && size < * )
{
strTotalSize = (size / ).ToString() + " KB";
}
else
{
strTotalSize = (size / / ).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 = ;
downloadForm.Height = ;
downloadForm.MaximizeBox = false;
downloadForm.MinimizeBox = false;
downloadForm.ControlBox = false;
downloadForm.StartPosition = FormStartPosition.CenterScreen; Label label = new Label();
label.Left = ;
label.Top = ;
label.Width = ;
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 = ;
int count = len / offset;
for (int i = ; 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+, p1.Y+);
}
#endregion
}
}
重点是libfuncs中的libfuncs.h、ChromeApp.h、ChromeApp.cpp、ClientAdapter.h、ClientAdapter.cpp五个文件,实现方法都在里面,其他的文件基本都是接口文件。我自己只根据项目实际需求来做的功能,接口没有全部实现。这个部分大家看源代码吧,我用到的地方都注释了。文采不行,写不动,大家原谅。睡了。
最后说句,我们公司网页美工解脱了!
基于Chromium构建Chrome WebBrowser for .net 控件(还有点心得体会)的更多相关文章
- 如何通过JavaScript构建Asp.net服务端控件
摘要 虽然ASP.NET的服务器控件一直被大家所诟病,但是用户控件(ACSX)在某些场景下还是非常有用的. 在一些极特珠的情况下,我们会使用JavaScript动态的构建页面中的控件,但假设遇到了我要 ...
- 基于jquery的可查询多级select控件(可记录历史选择)
一.功能和使用 公司有功能需求,还要一条代码引入的控件,网上找完全符合的控件比较难,寻找所花的时间还不如自己写一个,所以找个空闲时间自己写了一个 控件功能:1.可手动输入查询,也可点击下拉框查询, ...
- 基于avalon+jquery做的bootstrap分页控件
刚开始学习avalon,项目需要就尝试写了个分页控件Pager.js:基于BootStrap样式这个大家都很熟悉 在这里推荐下国产前端神器avalon:确实好用,帮我解决了很多前端问题. 不多说了,代 ...
- 文档驱动 —— 表单组件(五):基于Ant Design Vue 的表单控件的demo,再也不需要写代码了。
源码 https://github.com/naturefwvue/nf-vue3-ant 特点 只需要更改meta,既可以切换表单 可以统一修改样式,统一升级,以最小的代价,应对UI的升级.切换,应 ...
- 基于C#语言MVC框架Aspose.Cells控件导出Excel表数据
控件bin文件下载地址:https://download.csdn.net/download/u012949335/10610726 @{ ViewBag.Title = "xx" ...
- 基于WPF系统框架设计(10)-分页控件设计
背景 最近要求项目组成员开发一个通用的分页组件,要求是这个组件简单易用,通用性,兼容现有框架MVVM模式,可是最后给我提交的成果勉强能够用,却欠少灵活性和框架兼容性. 设计的基本思想 传入数据源,总页 ...
- 基于 mpvue 框架的小程序选择控件,支持单列,多列,联动
最近在学着写mpvue小程序,在做选择控件时候遇到了点问题,按照微信小程序方法picker,很不方便! 在网上搜到一个很好用的组件下面给大家分享: 组件说明文档链接:https://go.ctolib ...
- chrome新版安装flash控件失败解决方法
今天chrome打开后出现插件过期,之后更新一直安装失败 度娘找到一个方法: 1.下载flash最新版for chrome : https://fpdownload.macromedia.com/pu ...
- 构建基于WinRT的WP8.1 App 03:Page控件
单页面模板 通常利用Visual Studio 2013创建的最简单的WP8.1应用是Blank App,它只包含一个不带任何UI的页面,并且没有任何状态管理的逻辑. 该不带任何UI的页面称为Blan ...
随机推荐
- 使用 IntraWeb (38) - TIWAppForm、TIWForm、TIWBaseHTMLForm、TIWBaseForm
窗体 TIWAppForm 所在单元及继承链: IWAppForm.TIWAppForm 主要成员: property OnURLRequest: TOnURLRequest //响应下面的 DoUR ...
- unreal3脚本stacktrace的问题
在unrealscript里获取调用栈,有下面两函数: /** * Dumps the current script function stack to the log file, useful * ...
- UE简单操作
一:UE恢复默认配置 1.开始--运行-- “%APPDATA%” 回车 . 2. 找到并打开 IDMComp 文件夹.将文件夹“UltraEdit”整个给删除. 3.重 ...
- 信心题--FUOJ2226(莫队算法)
http://acm.fzu.edu.cn/problem.php?pid=2226 信心题,还说是信心题,题目给的真好.但是一点都不像信心题. 又是一个新的算法,莫队算法 莫队算法是一个用数组就可以 ...
- 7.5 [bx+idata] 书中错误
这节中的问题 7.1 有错误 题目和我自己的注释为: 用 Debug 查看内存,结果如下: 2000:1000 BE 00 06 00 00 00 ... 写出下面程序执行后,ax,bx,cx中的内容 ...
- noip2014-day1-t2
题目描述:无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离. ...
- Servlet中使用Log4j2
因为Servlet常用的版本有两个,即2.5与3.0.要在web application中使用Log4j2,还需要加入log4j-web的jar包.log4j通过web.xml中的context参数l ...
- [SmartFoxServer概述]Zones和Rooms结构
Zones和Rooms结构: 相对于SFS 1.X而言,在Zones和Rooms的配置上,SFS2X有了显著的改善.尤其是我们建立了房组这样一个简单的概念,它允许在一个逻辑组中管理Rooms,从而独立 ...
- 用scala实现一个sql执行引擎-(上)
前言 在实时计算中,通常是从队列中收集原始数据,这种原始数据在内存中通常是一个java bean,把数据收集过来以后,通常会把数据落地到数据库,供后面的ETL使用.举个一个简单的例子,对一个游戏来说, ...
- canvas放射性渐变填充
今天在学习canvas时,遇到canvas的fillstyle有一个createRadialGradient()方法,创建放射性渐变. 上代码: <!DOCTYPE html> <h ...