分类
CSharp|C# Web|网页

使用CEF作为桌面应用UI开发框架的分析

在本人编写的几个上位机程序中,程序界面部分多数使用WinForm作为开发框架。WinForm框架的基本容器单位为窗体,即“Form”。每个窗体中包含了若干个不同种类的控件,即“Control”。控件分为数据类、容器类等。窗体和控件执行了不同的事件,如“单击”、“右击”等,会触发相应的回调函数,以此实现交互功能。创建每个窗体后,按照框架预定义的规则会生成Form.designer.cs及Form.cs两个不同的文件。这两个文件实质上属于同一个类(Class),由partial关键词划分。前者主要包含了该窗体及其中的控件的声明和定义代码,由框架自动生产。后者为使用者实际编码处理具体逻辑区域。

使用WinForm的好处是在界面外观要求不高的情况下,可以十分快速地在所见即所得式的界面编辑器通过拖拽控件的方式实现界面布局。然后在控件属性页中静态地定义每个控件的各项属性,在控件事件页中指定控件能够产生的每个动作对应的回调函数。以上方便之处,得益于WinForm框架预先已对这些控件作出了标准化处理。

当需要实现较为高级的界面功能时,WinForm的弱点就会凸显出来。如当程序运行于不同分辨率屏幕的时候,界面往往不能与在生产环境中保持一致。当某个控件中的文本内容过长时,会超出容器边界或被右方控件遮盖。正确的处理方法应该是将超出部分文本换行,并下移下方控件位置。

还有许多如界面主题、动画效果等高级界面功能在WinForm中较难实现。虽然从编程角度上看,这些功能的确可以通过对WinForm进行底层绘图级别的扩展和补充一一实现,但是解决这些属于框架先天设计的问题往往需要耗费大量的时间和精力。

引入CEF(Chromium Embedded Framework)作为应用程序UI框架。CEF实质上是一个谷歌浏览器的内核。与WinForm框架中自带的WebView控件不同的是,WebView的内核是IE,对HTML特性的支持十分糟糕。而CEF的WebKit内核能够良好地支持HTML 5特性。

得益于互联网尤其是移动互联网的快速发展和社区的力量,以Bootstrap、Foundation和Semantic UI为代表的一系列开源前端框架已经在HTML+CSS+JS的体系中将UI功能开发得十分成熟。其常用控件种类上能够基本满足桌面程序的要求。从美观角度上看,这些标记式的界面框架完全优于一众传统的代码式的界面框架。从运行性能上看,以Web为主要应用场景的UI实现方式天生需要适应不同的软硬件环境,所以在性能优化上已经有了长足的积累,在处理复杂特效时有比同样是标记式界面框架的WPF有更多的优势。

CEF代替Form作为前端界面的容器,界面由WEB技术实现,而事务逻辑仍然由C#实现这种开发方式与移动端的Hybird APP有着很多相似之处,后者已经以WEB+JAVA或WEB+Objectiv-C的实现方式得到了业界的大量运用。

分类
Web|网页

反文艺系列-1

有段时间我发现有的人喜欢用about:blank404 Not Found或者其衍生写法做网名。

很明显是受到了各浏览器的启发,以示一种淡淡的忧伤。颇有一股赛博文艺Feel.

现在用任何可以通过昵称搜人的应用,仍然可以搜出一堆使用类似网名的人。

 

先来八一八除了about:blank还能about些什么。

about:DesktopItemNavigationFailure 已取消网页导航

about:Home 主页

about:InPrivate IE隐私模式介绍页

about:NavigationCanceled 已取消网页导航

about:NavtigationFailure 已取消网页导航

about:NoAdd-ons IE无加载项运行模式提示页

about:NoAdd-onsInfo IE加载项介绍页

about:PostNotCached 网页已过期

about:SecurityRisk 安全设置风险提示页

除IE外的其他浏览器并没有兼容about:blank之外的这些统一资源定位符。

 

再说一说404。404是HTTP状态码的一种。这个类目相当的多,只挑几个除了404外比较常见的说。

301 Moved Permanently 请求页面被永久移动到新位置

403 Forbidden 访问了一个没有权限去访问的位置

500 Internal Server Error 服务器错误,动态网页服务程序有异常

502 Bad Gateway 通常是服务器挂了

各位赛博文艺青年除了用about:blank和404来表示自己的感情空窗期外,是不是还多了很多选择来表示自己的不同状态呢?(笑

分类
Web|网页

[BlogEngine.Net] 适配富文本编辑器到BE

一、需求分析

本博客的博客程序使用的是BlogEngine.Net 3.3.5.0(截至2017/1/20,以下简称BE),由于后台发布文章页面的富文本编辑器不能满足我的使用需求。我决定适配一个别的富文本编辑器到BE。

二、资料搜集

BE是有44万多行实际代码的项目,一开始我先从BE的项目目录结构着手。大致分析了一下各个一级、二级目录下文件的作用。然后就是从某一个功能点切入,上下梳理出实现该功能的业务流程。

因为对于体量那么大的项目,如果从程序入口开始一步步的分析,将会非常耗时。而且很有可能分析了很久都还在后台逻辑代码上,没有前端的表示,很难去理解一个个的变量和函数有何作用。所以我决定从各个可见的功能点入手来解剖整个项目,表现在这篇文章上就是为BE添加一个富文本编辑器。

光靠自己一时也很难找出实现这个功能的关键代码在哪里,所以最好的方法就是找一下官方有没用关于这个的wiki。

虽然官方的wiki涵盖的功能不多,所幸这个功能还是有写的。BlogEngine.net:adopting-ckeditor

这篇wiki的发布日期是2014/5/22日,当时的版本还是2.9,所以很多目录结构和代码已经和3.3不一样。

以下的步骤仅适用于3.3版本。

三、适配步骤

1.下载富文本编辑器包。

本文中使用的富文本编辑器是CKEditor,CKEditor:下载地址。该编辑器有多个含有不同功能的集合包可供选择,读者根据自己需求下载。我使用的是Full Package原则上任何集合包都适用于以下步骤。

2.解压编辑器包。

解压编辑器包后,可以发现该编辑器的实现语言使用的是Javascript,并有少量CSS和HTML文件作为实体和框架。

细想一下编辑器的特征:一、编辑器是嵌入到HTML页面中使用的。二、编辑器的功能实现是于后台无关的。可以明白使用JS的原因。

3.添加编辑器包到BE工程。

将 ..\ckeditor_4.6.2_full\ckeditor 放到 ..\BlogEngine\BlogEngine.NET\admin\editors 文件夹下。后者是BE工程中专门存放不同编辑器实现代码的目录。

构成 ..\BlogEngine\BlogEngine.NET\admin\editors\ckeditor 的目录结构。

然后用Visual studio打开解决方案,把编辑器包放进解决方案的对应位置。

4.适配编辑器到BE工程。

经过第3步后,编译环境已经可以调用到编辑器包中的资源了。但是BE工程实际上还感知不到编辑器的存在。

编辑器并非仅为BE工程设计的,它也可用于其他Web项目。为了达到这种目的,有两种解决方法:一,编辑器的设计者根据使用它的项目编写上层的API,这样把编辑器包复制进不同的项目后就可以直接使用。二,编辑器在中层预留个接口,由使用它的项目的设计者根据自身项目的不同来实现这个接口。

显然,第二种方法更符合现实。这就需要一个适配的过程。

BE的开发者规定了编辑器的适配方法,并在Wiki中进行了说明。

4.1构建实体元素文件

在 ..\BlogEngine\BlogEngine.NET\admin\editors\ckeditor 目录下新建一个editor.cshtml文件。文件中输入以下代码。
<textarea cols="100" id="editor1" name="editor1" rows="10" data-sample="1"></textarea>

实际上,任何使用到编辑器的页面都会从配置文件中找到cshtml文件,然后把该行代码嵌入到自己的代码中。

因此,该代码是作用是让编辑器作为一个HTML元素表现在页面中。

4.2实现读写接口

在 ..\BlogEngine\BlogEngine.NET\admin\editors\ckeditor 目录下新建一个editor.js文件。文件中输入以下代码。

var editorGetHtml = function () {
 return editor.getData();
}
 
var editorSetHtml = function (html) {
 editor.setData(html);
}
 
 
var editor = CKEDITOR.replace('editor1', {
 height: 400
});

编辑器的主要作用是以所见即所得的模式代替手写HTML标签的方式展现和编辑各种图文元素。而实际上,要在网页上显示图文,其本质还是HTML标签。

读接口的作用是让编辑器把所见即所得的图文转换为HTML标签代码传递给BE。反而言之,写接口的作用是BE把已有的HTML标签代码传递给编辑器,以所见即所得的方式展现和编辑。

因此,不论使用何种编辑器,都需要去实现读写接口。这里还多了一步是把编辑器实体元素转换为JS变量。以供读写函数去访问实体元素中存储的HTML标签代码。这一步根据编辑器实现方式的不同,可能不是必须的。

这部分JS代码的编写应该参照编辑器给出的API解释。

5.更改配置文件

经过上面的步骤,CKEditor已经可以被BE使用。但是BE同时带着几个不同的编辑器,因此还需要指定CKEditor为默认使用的编辑器。

在 ..\BlogEngine\BlogEngine.NET\web.config 中的<appSettings>子节点下,添加新的键值对代码。

<add key="BlogEngine.DefaultEditor" value="~/admin/editors/ckeditor/editor.cshtml"/>

指明包含了默认编辑器实体元素文件的位置。

6.嵌入JS代码

由我们实现的读写接口的代码可知,JS代码是紧跟着HTML元素的。从cshtml文件中可以发现没有把JS嵌入到页面的操作。因此还需要一个嵌入JS代码的步骤。

打开 ..\BlogEngine\BlogEngine.NET\AppCode\App_Start\BlogEngineConfig.cs 文件。可以发现有一段代码是用于根据配置的默认编辑器的不同,嵌入不同编辑器JS代码的。但是原来的该段代码逻辑上是并不能实现这个功能的,应该改成下面的样子。

if (BlogConfig.DefaultEditor == "~/admin/editors/bootstrap-wysiwyg/editor.cshtml")
{
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/bootstrap-wysiwyg/jquery.hotkeys.js");
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/bootstrap-wysiwyg/bootstrap-wysiwyg.js");
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/bootstrap-wysiwyg/editor.js");
}
else if(BlogConfig.DefaultEditor == "~/admin/editors/ckeditor/editor.cshtml")
{
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/ckeditor/ckeditor.js");
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/ckeditor/editor.js");
}
else if (BlogConfig.DefaultEditor == "~/admin/editors/tinymce/editor.cshtml")
{
 // tinymce plugings won't load when compressed. added in post/page editors instead.
 //bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/tinymce/tinymce.min.js");
 //bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/tinymce/editor.js");
}
else
{
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/summernote/summernote.js");
 // change language here if needed
 //bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/summernote/lang/summernote-ru-RU.js");
 bundles.GetBundleFor("~/scripts/wysiwyg").Include("~/admin/editors/summernote/editor.js");
}

四、总结

完成适配步骤后,编译整个工程,进入文章编写页面后,可以发现富文本编辑器已经从TinyMCE变成了CKEditor。