本文介绍了在K/3 Cloud平台上如何实现第三方设备的二次开发接入。由于K/3 Cloud平台不能涵盖所有第三方接入驱动,因此通过开放桌面端接口模型,使这些设备能够接入。文章详细说明了二次开发组件的部署步骤,包括组件的压缩、部署到服务器目录、配置注册文件以及启动客户端进行组件下载等。同时,还介绍了如何在Visual Studio中开发客制化组件,包括新建类库工程、引用基础组件、编写客制化控件桌面客户端代码以及服务端表单插件代码。此外,还提供了两种在BOS IDE中配置客制化控件的方法,包括直接在BOS IDE中拖放自定义控件和使用构造插件方式实现客制化。
一、应用背景
在企业应用中广泛存在一些接入设备,如电子行业的激光扫描设备,IC卡刷卡设备,医疗行业的电子秤,射频读卡器,影像设备等。接口类型没有同一标准,有的采用RS232,RS485,USB等,有的采用FTP文件服务器,有的采用Smart Card接口组件,驱动设备也各行其是。K/3 Cloud作为一个通用平台,不能包罗所有的第三方接入驱动。因此,我们开放一个桌面端的接口模型,实现这些设备的二次开发接入。
二、部署二次开发组件
1、在K/3 Cloud服务器安装目录的【WebSite/ClientBin/CustomControl/WPF】目录下部署二次开发组件,这个目录安装包会创建。
自定义组件可以用Zip压缩文件压缩为Zip格式,后修改后缀名为.kdz。
1.1、每个组件包括引用组件单独压缩为zip格式的文件,例如:a.dll -> a.zip;
1.2、修改后缀名为.kdz,例如:修改a.zip 文件为 a.dll.kdz;
1.3、配置配置文档,参见 2 或3.1
1.4、部署是注意IIS站点中是否存在MIME类型为.kdz的,如果没有,需要手工加入MIME类型如下:.kdz,application/x-msdownload ;
2、BOSIDE中拖放自定义控件,在自定义控件的属性列表中找到【引用组件】属性,把相关参考组件注册到该属性里面。相关依赖组件注册在BOSIDE中的菜单【文件--引用组件管理】功能进行关联依赖组件注册;(6.0以后推荐这种方式);如果更新,则修改参考组件列表中对应组件的版本信息即可。(推荐方案)
如果使用了这个方法,那么就不需要做下面“3、配置MainDownloadList.xml注册文件(备选方案)”这个步骤了,该步骤可以忽略。
3、配置MainDownloadList.xml注册文件(备选方案)
3.1、运行记事本编辑器,打开安装目录【WebSite/ClientBin/CustomControl/WPF/MainDownloadList.xml】的文件。
手工拷贝一个节点(一个文件需要一个节点进行注册如下图)。然后修改绿色的文件名为你的组件名就好了。
2018-09-25 上传
下载附件 (360.41 KB)
客制化控件部署配置文档
3.2、如果有组件需要更新,请拷贝相关组件到【WebSite/ClientBin/CustomControl/WPF】目录下,并执行上面3.1的步骤,注意更新组件的动作仅在客户端启动时运行一次。同时需要修改注册文件的版本号,以便客户端能够更新到最新组件,如下图:
4、启动K/3 Cloud客户端桌面版程序,这时程序会自动按照服务器地址设定检查组件版本,并按需要下载相关客制化组件到本地的 【我的文档\Kingdee\K3Cloud\controlplugins】目录下。(这个目录由于原来是ClickOnce模式下权限限制的需要,现在在2015-05-07号补丁后就失效了, 按多数客户的需求将把客制化组件目录调整到Kingdee.BOS.Xpf.App.exe执行文件所在目录的子目录controlplugins下,一般会在【%Program Files %\Kingdee\K3Cloud\DeskClient\K3CloudClient\controlplugins】这个位置里面,方便二开程序定位自己的数据文件。)
5、客制化控件关联引用需要部署到controlplugins,或者需要引用K3CloudClient目录组件时,可以配置Kingdee.BOS.XPF.App.exe.config文件增加如下配置(6.0或5.0 2015-07-09补丁已经默认配置,如没有成功配置,可以手工添加):
<?xml version="1.0"?>
< configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="controlplugins"/>
</assemblyBinding>
</runtime>
< /configuration>
三、客制化组件开发
1、在Visual Studio 2012中新增类库工程;如果需要新增有WPF界面元素的,必须新建WPF类库类型的工程。(注意,如果使用的控件是32位平台的,那么客制化组件工程文件指定目标平台为x86)
2、引用服务器安装目录下【WebSite/ClientBin/CustomControl/WPF/Kingdee.BOS.XPF.ControlPlugins.Contracts.dll】基础组件;
3、新建类,编写客制化控件桌面客户端代码如下:
/// <summary>
/// 有界面插件,如果不需要界面展示的,无须继承任何FrameworkElement的子类
/// </summary>
public class SmartCardReader : ContentControl, IKDCustomControl
, IDynamicFormSupported
{
/// <summary>
/// 实现单据代理接口,以便通过this.FormProxy.ControlFactoryProxy.GetCmp(Key)即可获取到其他单据上其他字段的Proxy,从而控制其他字段的内容。接口IDynamicFormSupported 来自Kingdee.BOS.Client.Core.dll(2015-06-18以及之后的补丁提供该接口);如果不需要操控表单其他控件元素,就不需要实现这个接口。(可以通过this.FormProxy.ControlFactoryProxy.GetCmp(控件标识).GetControl()获取控件实例,实现定制交互逻辑);
/// </summary>
public IKDDynamicFormProxy FormProxy { get; set; }
public IKDCustomControlProxy Proxy { get; set; }
TextBlock _txtDsp = null;
protected void FireOnCustomEvent(CustomEventArgs e)
{
if (this.Proxy != null)
{
this.Proxy.FireCustomEvent(e);
}
}
/// <summary>
/// 界面组件初始化,如果没有界面要求,这里保留为空即可。
/// </summary>
public void InitComponent()
{
var label = "This is Custom control in Kingdee.XPF.CustomControlPlugins, named SmartCardReader";
this._txtDsp = new TextBlock()
{
ToolTip = label,
TextWrapping = System.Windows.TextWrapping.Wrap,
Text = label
};
this.Content = new Border()
{
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Colors.Blue),
Child = this._txtDsp
};
}
/// <summary>
/// 用于资源释放,在单据关闭时调用
/// </summary>
public void Release()
{
this.Content = null;
}
///************以下接口按自己需要添加*************////
/// <summary>
/// 定制控件入口,服务端调用方法:this.View.GetControl("FCUSTOMCONTROL").InvokeControlMethod("DoCustomMethod","WriteString",args),args是对象数组
/// </summary>
/// <param name="data"></param>
public void WriteString(string data)
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
if (this._txtDsp != null)
{
this._txtDsp.Text = data;
this.Proxy.FireCustomEvent(new CustomEventArgs("", "Success", "{message:'WriteString() Success!'}"));
}
else
{
this.Proxy.FireCustomEvent(new CustomEventArgs("", "Error", "{message:'WriteString() Error, component uninitilization!'}"));
}
}));
}
}
4、编写客制化控件服务端表单插件代码如下:
4.1、在BOS IDE中拖放自定义控件:(推荐方案)
现在在2015-05-07号补丁后, 下面4.2.1,4.2.2的步骤就可以省略了。直接在BOSIDE中拖放一个【通用控件--自定义控件】,然后填写属性【程序集】,【类名】,【命名空间】就可以了。(这个方法将替代4.2.1,4.2.2两个步骤)
客制化控件配置属性
如果相关参考依赖组件,可以在BOSIDE的菜单【文件】-【引用组件管理】中设置,如下图:
2018-09-25 上传
下载附件 (206.85 KB)
客制化控件配置引用
(论坛上传图片失败,等好了上传,设置比较简单,界面上也有说明!)
4.2、编写构造插件方式实现客制化:(备选方案)
4.2.1、新建动态表单,拖入面板控件,命名为【FCustomControl】。
4.2.2、编写客制化控件服务端表单构建插件,如下代码:
/// <summary>
/// 测试自定义控件构造插件
/// </summary>
public class TestCustomControlCreatePlugIn : AbstractDynamicWebFormBuilderPlugIn
{
public override void CreateControl(CreateControlEventArgs e)
{
base.CreateControl(e);
if (e.ControlAppearance.Key.EqualsIgnoreCase("FCustomControl"))
{
e.Control["Assembly"] = "Kingdee.XPF.CustomControlPlugins";
e.Control["ClassName"] = "SmartCardReader";
e.Control["NameSpace"] = "Kingdee.XPF.CustomControlPlugins";
e.Control["SourcePath"] = "\\ClientBin\\CustomControl\\WPF\\Kingdee.XPF.CustomControlPlugins.dll";
e.Control["xtype"] = "kdcustomcontroldef";
}
}
}
4.3、表单业务插件,如下代码(以Python为例,可以自行翻译为C#代码):
C#代码例子:
using Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
namespace Kingdee.Texst.CustomControl
{
[Description("测试客制化控件业务插件")]
public class CustomControl : AbstractBillPlugIn
{//实现定制控件的自定义接口调用
public override void CustomEvents(CustomEventsArgs e)
{
base.CustomEvents(e);
if (e.Key == " FCUSTOMCONTROL ")
{
var recData = e.EventArgs;
}
}
public override void ButtonClick(ButtonClickEventArgs e)
{
if (e.Key == "FWRITESTRING")
{
var str1 = this.Model.GetValue("FTEXT");
var args = new object[1];
args[0] = str1;
//#实现定制控件的自定义接口调用
this.View.GetControl("FCUSTOMCONTROL").InvokeControlMethod("DoCustomMethod", "WriteString", args);
// 特别注意:这里的args参数应对是一个对象数组,在C#中如下编码;
//0个参数的情况: var args = new object[1]; args[0] = null;
//1个参数的情况: var args = new object[1]; args[0] = str1;
//2个参数的情况: var args = new object[2]; args[0] = str1; args[1] = str2;
//n个参数的情况: var args = new object[n]; args[0] = str1; args[1] = str2; ... args[n-1] = strn;
}
}
}
}
python代码例子:
from System import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
def ButtonClick(e):
if(e.Key=="FWRITESTRING"):
str1 = this.Model.GetValue("FTEXT")
args =[ str1]
#实现定制控件的自定义接口调用
this.View.GetControl("FCUSTOMCONTROL").InvokeControlMethod( "DoCustomMethod", "WriteString", args)
#特别注意:这里的args参数应对是一个对象数组,在C#中如下编码;
#0个参数的情况: var args = new object[1]; args[0] = null;
#1个参数的情况: var args = new object[1]; args[0] = str1;
#2个参数的情况: var args = new object[2]; args[0] = str1; args[1] = str2;
#n个参数的情况: var args = new object[n]; args[0] = str1; args[1] = str2; ... args[n-1] = strn;
#这里获取服务端发送的数据
def CustomEvents(e):
if(e.Key==" FCUSTOMCONTROL "):
recData = e. eventArgs
四、屏蔽其他端使用该表单:
因为本功能仅允许在桌面使用,其他端必须进行功能屏蔽。编写业务插件如下:
public override void PreOpenForm(Core.DynamicForm.PlugIn.Args.PreOpenFormEventArgs e)
{
if (e.Context.ClientType != ClientType.WPF)
{
e.CancelMessage = "不支持桌面应用的功能,请在桌面应用上使用该功能。";
e.Cancel = true;
}
base.PreOpenForm(e);
}
[或者]显示界面,但不加载第三方ActiveX控件,选择动态加载ActiveX组件的方法。
就是初始化时不加载,控件实现InitThirdPart()方法来初始加载ActiveX控件(类似例子中的WriteString方法),由服务端调用InitThirdPart()才加载控件。
服务端业务插件中AfterBindingData接口重载后判断this.Context.ClientType==ClientType.WPF就调用InitThirdPart()方法,否则就不调用。客制化控件就是给你自己灵活使用的,自己变通想下就可以解决.
容易出错的情况参考帖子:
推荐阅读