动态生成用户定义界面(第 1 部分)






4.35/5 (10投票s)
介绍我们如何解决允许用户定义数据结构和编辑这些数据的用户界面的问题。
引言
最近,我们的公司遇到了一个项目,该项目需要超级用户能够配置资产存储的数据,以及资产在网格、报表和数据录入表单中的显示方式。换句话说,业务对象属性、数据库数据和用户界面表单都必须能够由客户现场的超级用户定义和自定义。需要动态生成用户界面,以适应每个组织设置的资产属性和分组。
下面是一个在系统中定义“送货车”资产类型的示例
用户能够创建属性组(属性组只是将在表单上一起显示的属性集),将属性分配给这些组,并更改每个组和组内每个属性的顺序。此外,对于每个属性(或资产的属性),用户能够定义数据类型以及控制属性值的任何规则(在这种情况下,最大值、最小值以及是否为必填项),以及任何查找列表值。例如,选定的资产类型送货车有三个信息组,在型号详情组中,我们发现四个属性。选中的是型号(年份),它是一个必填的整数字段,并设置了最小值和最大值。通过这种资产定义,系统会创建一个屏幕,用于捕获送货车的详细信息。
此表单在运行时动态创建,包括显示必填字段的指示,具有指示违反属性设置规则的错误提供程序,具有基于属性类型(例如,为制造商字段提供的组合框)的正确控件类型,以及用于将属性分组的面板。
开发一个允许用户为每个业务对象定义用户界面以及存储数据和数据捕获规则的应用程序,通常被认为非常复杂。从我们的角度来看,最令人印象深刻的部分是,这些表单不是为这个特定项目手工编写的——相反,它们利用了我们在 Habanero 企业应用程序框架(简称 Habanero)中多年使用的表单构建功能。
使用代码
让我继续讲解代码示例中的例子。过去,我们在制造业中遇到过一个非常相似的想法,当时需要对工厂的机器类型进行建模,而且新机器、升级机器和新工艺会相当频繁地添加,这使得静态创建每个用户界面变得不可能。
请注意,代码示例并非来自生产应用程序——它只是在几个小时内 put together 的一个示例。它未经彻底测试,并且存在一些已知问题:它被 put together 来展示正在讨论的核心概念。它还使用内存中的数据存储来简化分发,但如果需要,可以将其替换为真正的数据库。它使用了 Habanero Enterprise Application Framework(它是开源的),但所有必需的 DLL 都包含在 zip 文件中。
如果您在示例中打开解决方案,并查看主项目中的类图,您应该会看到类似这样的内容
在此示例中,机器类型由机器属性定义集合定义。如果我使用铝轧机的例子,这些属性可能是最大宽度、最大输入厚度、最大输出厚度等等。捕获的每台机器都将具有一个机器类型,并将接收该机器类型定义中定义的所有属性。如果将其映射到传统的面向对象原理,您可以将机器类型和机器属性定义视为“类”,将机器和机器属性视为实例化的“对象”。
在示例应用程序中,我编写了一些代码来在程序启动时设置一些测试数据——两种机器类型,以及每种机器类型的几个属性。
下面的屏幕(出现在“数据 | 机器类型”下)显示了机器类型的定义。在该屏幕中,您可以看到“喷绘机”机器类型有三个配置的属性定义,其中一个是必填属性。
然后,如果您转到“数据 | 机器”并选择添加一台“喷绘机”,将会弹出一个类似下图所示的表单。该表单包含上面定义的属性,并具有完整的验证功能,这意味着如果您在未输入数据的情况下尝试按“确定”,您将收到相应的错误。
如果您在创建这台新的“喷绘机”之前向“喷绘机”机器类型添加更多属性定义,新的字段也会显示在表单上。
深入技术细节
现在,进入技术细节:Habanero 的 UI 层在其核心处有一个描述表单结构的面向对象的模型
这个简化的类图表明了 Habanero 如何将表单建模为一组字段。每个字段都包含实例化正确控件以及将指定业务对象的属性映射到控件(反之亦然)所需的映射器所需的信息。Habanero 的 UI 层能够接收这样的结构并生成我们上面看到的表单。UI 层还能够将该表单映射到特定的业务对象,并将每个属性映射到特定的控件。
由于这种固有的能力,动态创建用户界面的任务就变成了为表单创建过程建模正确的数据结构。这可以通过遍历机器类型的属性定义并为每个定义创建一个 `UIFormField` 来轻松完成。
foreach (MachinePropertyDef machinePropertyDef in MachinePropertyDefs) {
UIFormField uiProperty =
new UIFormField(null, machinePropertyDef.PropertyName,
"TextBox", "System.Windows.Forms", "",
"", true, "", new Hashtable(), null);
form.Add(uiProperty);
}
传递给 `UIFormField` 构造函数的各种参数只需填充类图中显示的字段。在示例代码中,我对所有字段都使用了 `TextBox` 控件,因为目前我只支持字符串和整数,但在实际的资产管理示例中,我们提供了所有常用控件。系统根据数据类型确定默认控件。根据用户设置的字段数据类型选择正确的控件非常简单。
一旦设置了此结构并将其注册到相应的机器类型,Habanero 的控件(如网格、表单生成器等)就会直接使用它。
这个项目展示了使用一个框架的强大功能和可扩展性,该框架将表单定义和业务对象定义维护为一个对象,因为它们的结构和行为可以根据输入数据在运行时进行修改。
当然,如何将控件映射到业务对象属性、如何根据定义表创建业务对象属性以及如何为每个业务对象加载规则等问题仍然存在。请继续关注明天的第二部分,我将更详细地讨论示例项目代码,并解释从 UI 层到 BO 层再到数据库的映射。
链接
历史
- 2008 年 11 月 26 日:添加了文章,纠正了一些排版错误。
- 2008 年 11 月 27 日:为可能感兴趣的人添加了 Habanero 的链接。