FineUI 官方论坛
标题: FineUIPro+CEFSharp的集成案例 [打印本页]
作者: 一尺野光 时间: 2018-8-17 11:51
标题: FineUIPro+CEFSharp的集成案例
FineUIPro+CEFSharp的集成案例
一、 起源 经过N个月的努力,以FineUIPro帮助客户开发并部署企业应用系统。
在进行试运行阶段之时,突然,客户提出一个需求:
甲:你们的系统怎么在浏览器中进去的?这样,没有一点企业形象,你看看还360、IE的,太Low了!这样不行滴,要在桌面上有个公司LOG的快捷键,这样才能体现企业光辉形象。
乙:哦,那我们做一个浏览器的快捷键,怎么样?
甲:不行的,那还是在浏览器中,还是LOW。
甲:你们这种是B/S、我们要的是C/S!
乙:……(狂吐)
甲、乙:……(N个回合沟通,讨论B/S、C/S的利弊)
结论:甲胜、乙输。做C/S。
二、 对策 不可能重码代码,只能想变通之道。
乙问度娘:度娘啊,B/S转C/S的方案有没有?
度娘答:没有!这里有B/S转SB倒有,要不一试?
只能再找方案。(N时……)
乙准备上吊之时,119出场:CEFSharp
方案:
做一个集成CEFSharp的WinForm程序,实现内嵌B/S。
效果:
B/S成果仍然保留,留住心血;
不依赖各种浏览器,可以在桌面上做快捷键,体现企业形象。
CEFSharp的解决之道与优势:
度娘:
CEFSharp支撑与JS相互调用;
另外我可以告诉你很多哦!
乙:滚,早干嘛去了……
三、 意外收获 由于之前系统中需要用到多个ActiveX插件(例:单据插件、FastReport报表插件),因此,系统只能在IE浏览器中使用,无法使用Chrome浏览器,存在IE的各种性能问题,现在借助CEFSharp,则可以抛弃IE浏览器,采用Chrome内核,一方面:提升性能;另一方面:通过UserControl集成ActiveX,并且为后续可以做更多的扩展支撑。
四、 成果展示
(一) 项目方案
(二) 桌面快捷键
(三) 系统界面
1. 首页
2. 业务页
3. 调用单据插件ActiveX
4. 报表设计页
5. 报表预览页
作者: sanshi 时间: 2018-8-17 17:08
不错,有想法。那个ActiveX没看懂:是啥意思
作者: leetle 时间: 2018-8-17 18:41
导出表格时,无响应。有遇到没?
作者: 一尺野光 时间: 2018-8-18 09:15
该问题归根到底是CefSharp对文件下载的控制问题,网上有介绍需要重构CefSharp的接口来支持。而我的思路则是比较简单,通过CefSharp与Js的互调控制,来变通实现文件的下载。
需要做几处调整:
1、通过CefSharp打开网站时,需要标注该项目是运行在CefSharp中,我的方式是在打开网站的网址中,增加一个参数,保存到Session中
if (Request.QueryString["Container"]!=null)
{
Session["Container"] = Request.QueryString["Container"].ToString();
}
2、针对导出的处理
2.1、在CefSharp的WinForm中,增加一个类,用于响应网站的Js调用,里面有一个下载保存的处理
2.1.1、定义一个ryCefExtendFunCls的类,里面放置针对文件Url,通过Http下载的控制
/// <summary>
/// Http方式下载文件
/// </summary>
/// <param name="AUrl">http地址</param>
/// <param name="ALocalFile">本地文件</param>
/// <returns></returns>
public bool CallDownloadFromHttp(string AUrl, string ALocalFile)
{
bool flag = false;
long startPosition = 0; // 上次下载的文件起始位置
FileStream writeStream; // 写入本地文件流对象
// 判断要下载的文件夹是否存在
if (File.Exists(ALocalFile))
{
writeStream = File.OpenWrite(ALocalFile); // 存在则打开要下载的文件
startPosition = writeStream.Length; // 获取已经下载的长度
writeStream.Seek(startPosition, SeekOrigin.Current); // 本地文件写入位置定位
}
else
{
writeStream = new FileStream(ALocalFile, FileMode.Create);// 文件不保存创建一个文件
startPosition = 0;
}
try
{
HttpWebRequest myRequest = (HttpWebRequest)HttpWebRequest.Create(AUrl);// 打开网络连接
if (startPosition > 0)
{
myRequest.AddRange((int)startPosition);// 设置Range值,与上面的writeStream.Seek用意相同,是为了定义远程文件读取位置
}
Stream readStream = myRequest.GetResponse().GetResponseStream();// 向服务器请求,获得服务器的回应数据流
byte[] btArray = new byte[512];// 定义一个字节数据,用来向readStream读取内容和向writeStream写入内容
int contentSize = readStream.Read(btArray, 0, btArray.Length);// 向远程文件读第一次
while (contentSize > 0)// 如果读取长度大于零则继续读
{
writeStream.Write(btArray, 0, contentSize);// 写入本地文件
contentSize = readStream.Read(btArray, 0, btArray.Length);// 继续向远程文件读取
}
//关闭流
writeStream.Close();
readStream.Close();
flag = true; //返回true下载成功
}
catch (Exception)
{
writeStream.Close();
flag = false; //返回false下载失败
}
return flag;
}
/// <summary>
/// 调用保存文件对话框
/// </summary>
/// <param name="ADefaultFileName"></param>
/// <returns></returns>
private void CallShowSaveDialog()
{
SaveFileDialog mySaveFileDialog = new SaveFileDialog();
//设置文件类型
mySaveFileDialog.Filter = "全部文件(*.*)|*.*";
//保存对话框是否记忆上次打开的目录
mySaveFileDialog.RestoreDirectory = true;
//设置默认的文件名
mySaveFileDialog.FileName = _SaveFileDefaultName;
//点了保存按钮进入
if (mySaveFileDialog.ShowDialog() == DialogResult.OK)
{
_SaveFileName=mySaveFileDialog.FileName;
}
else
{
_SaveFileName="";
}
}
/// <summary>
/// 扩展:下载文件
/// </summary>
/// <param name="AUrl"></param>
public void downLoadFile(string AUrl)
{
ryAppConfigOperatorCls myAppConfigOperatorCls = new ryAppConfigOperatorCls();
AUrl = myAppConfigOperatorCls.GetAppRootUrl() + AUrl.Replace("_RYLJFG_", "/");
string sFileName = Path.GetFileName(AUrl);
_SaveFileDefaultName = sFileName;
Thread InvokeThread = new Thread(new ThreadStart(CallShowSaveDialog));
InvokeThread.SetApartmentState(ApartmentState.STA);
InvokeThread.Start();
InvokeThread.Join();
if (_SaveFileName != "" && CallDownloadFromHttp(AUrl, _SaveFileName))
{
MessageBox.Show("文件已成功下载!");
}
}
2.1.2、再在CefSharp的ChromiumWebBrowser中对其进行注册
public void InitiContainer(string AUrl)
{
var Settings = new CefSettings
{
Locale = "zh-CN",
AcceptLanguageList = "zh-CN",
CachePath = Directory.GetCurrentDirectory() + @"\Cache",
PersistSessionCookies = true,
MultiThreadedMessageLoop = true
};
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
Cef.Initialize(Settings);
_Browser = new ChromiumWebBrowser(AUrl);
GloableDefine._CallWebBrowseExecJs = DoExecWebBrowseScript;
pnlContainer.Controls.Add(_Browser);
_Browser.Dock = DockStyle.Fill;
//注册ryCefExtendFunCls JS对象
_Browser.RegisterJsObject("ryCefExtend", new ryCefExtendFunCls());
}
2.2、在网站的项目aspx页面中,增加一个JS,用于调用CefSharp中的下载保存方法
<script type="text/javascript">
function Call_RYCEF_Download(AUrl) {
//注意在js函数里面只认小写开头
ryCefExtend.downLoadFile(AUrl);
}
</script>
2.3、在网站的项目aspx页面后台代码中,根据Session的判断,去分别控制以哪种方式下载
public void btnDownload_Click(object sender, EventArgs e)
{
if (gcgzwjGrid.SelectedRowIndex < 0)
return;
string sPsflID = gcgzwjGrid.Rows[gcgzwjGrid.SelectedRowIndex].DataKeys[0].ToString();
string sTempFile = GetFile(sPsflID);
if (File.Exists(sTempFile))
{
if (Session["Container"] != null && Session["Container"].ToString() == "RYCEFCONTAINER")
{
//通过CefSharp接口下载
sTempFile = sTempFile.Substring(sTempFile.IndexOf("workfiles")).Replace("\\", "_RYLJFG_");
PageContext.RegisterStartupScript("Call_RYCEF_Download('" + sTempFile + "');");
}
else
{
//采用浏览器方式下载
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" +
Server.UrlEncode(Path.GetFileName(sTempFile)));
Response.TransmitFile(sTempFile);
}
}
}
2.4、由于在FineUiPro中,对于调用下载的按钮,需要控制Button参数(EnableAjax与DisableControlBeforePostBack),因此,在页面上的下载控件,需要有两个,一个用于常规浏览器使用,另一个特供CefSharp使用,前后端如下:
<f:MenuButton ID="btnDownload" runat="server" Text="附件下载" OnClick="btnDownload_Click" EnableAjax="false" DisableControlBeforePostBack="false">
</f:MenuButton>
<f:MenuButton ID="btnDownloadByCef" runat="server" Text="附件下载" OnClick="btnDownload_Click" Hidden="true">
</f:MenuButton>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (Session["Container"] != null && Session["Container"].ToString() == "RYCEFCONTAINER")
{
btnDownloadByCef.Hidden = false;
btnDownload.Hidden = true;
}
3、最终效果
图片传不上来
作者: leetle 时间: 2018-8-18 11:31
我项目中不用Session来解决问题。谢谢你的方案。
作者: 一尺野光 时间: 2018-8-18 11:36
你是在CefSharp中实现自己的Session,可以避免Session过期的问题
作者: Stark11 时间: 2019-7-27 23:42
这种技术方案是典型的逆势而下的,佩服你们的老板同意这个方案,果然有钱能使鬼推磨
作者: 刘军 时间: 2019-8-9 14:59
这种模式唯一的缺点是需要安装客户端及.net framework,其他方面完爆普通浏览器,可以使js有调用设备的能力,事实上很多项目都采用这种方案了,楼主这套系统采用ActiveX导致必须依赖IE的问题,反而一开始就应该采用这种方案
欢迎光临 FineUI 官方论坛 (https://fineui.com/bbs/) |
Powered by Discuz! X3.4 |