级别: 初级 Jeremy McGee (jeremy@mcgee.demon.co.uk), 独立 IT 顾问
2004 年 4 月 01 日 在这一由两个部分组成的系列文章中,我们向您展示了如何使用 Borland C#Builder Architect 中的 Borland 企业核心对象(Borland Enterprise Core Object,ECO)来为 DB2 Universal Database(UDB)构建由 UML 模型驱动的功能强大的应用程序。此第二篇文章展示了 ECO 如何可以快速地构建复杂的用户界面。
简介
Borland® C#Builder
TM
Architect 扩展了 C#Builder 的开发能力,使之能进行模型驱动的开发。通过自动实现部分统一建模语言(Unified Modeling
Language,UML)软件模型,节省了业务逻辑编码的时间。
在
第一篇文章
中,我们看到 C#Builder Architect 中的 ECO
TM
设计器和运行库框架是如何可以帮助快速实现应用程序的。这篇文章中,我将扩展该系列文章第一部分中所创建的简单应用程序,以使用户可以操纵我们创建的其他对象类。您还将看到如何使用通过关联获得的数据完整性选项。
可从
Borland
Web 站点获得 C#Builder Architect 的试用版。
添加人员
我们的 UML 图包括两种人员列表,每种都与我们在窗体上放置的 DataGrid 中的部门(department)相关联。接下来,您将在该窗体上添加两个网格,并创建一个简单的用户界面以便用户输入个人信息。
再向主 WinForm 添加两个网格,使之具有七列的宽度并留出左边间距。在这些网格下放置两个按钮,并将其 Text 属性设置为
Add Employee和
Add Candidate。稍后,您将为这些按钮添加程序代码。
图 1:空用户界面
从上至下将每个网格分别命名为
dgDepartment、
dgEmployee
和
dgCandidate。我们将访问
Department 数据网格(data grid)的当前记录,以筛选雇员(employee)和候选者(candidate)。
接下来,您将设置两个新的
ExpressionHandle,用以为这些网格获取数据。我们这次并非直接指向整个对象集合,而是通过合适的部门筛选出名称。为此,您会需要一个新的组件:
CurrencyManagerHandle。
CurrencyManagerHandle 返回数据绑定(data-bound)控件所指向的对象。因此这里,您可以使用 CurrencyManagerHandle
找到当前由 Department 网格所选择的部门。您还可以将 CurrencyManagerHandle 用作 ExpressionHandle
的根句柄。
从工具面板选择 CurrencyManagerHandle,并将之放置在窗体上。将其属性设置为:
Name: cmhDepartment
RootHandle: ehDepartment
BindingContext: dgDepartment |
这就是所需的所有工作。从此刻开始,您就可以使用表达式
(Department)cmhDepartment.Element.AsObject
来返回 Department 数据网格所指向的 Department 对象了。
现在,您可以为 Employee 和 Candidate 网格设置新的
ExpressionHandle。在窗体上放置两个
ExpressionHandle,并将其属性设置为:
Name: ehEmployee
RootHandle: cmhDepartment
Expression: employs
Name: ehCandidate
RootHandle: cmhDepartment
Expression: mightEmploy |
正如您将看到的,RootHandle 在这里被设置为 CurrencyManagerHandle,而非窗体的根句柄。以该方式级联 ECO
句柄是一种极其强大的技术。
然后,您可以将每个网格的 DataSource 属性设置为指向合适的 ExpressionHandle。编译该应用程序之后,将自动完成各列(注意,各个网格很可能直到进行了编译之后才显示正确的列):
图 2:将网格连接到其数据源
接下来为两个按钮添加代码,以便能够添加联系人。对于
Add
Employee按钮,其代码看上去与前一篇文章中用以添加 Department 的十分相似。但是这次有必要设置雇员(employee)的部门(department):
// Create a new Employee object
Employee theEmployee = new Employee(EcoSpace);
// Set the department of this employee to be the object selected
// by dgDepartment
theEmployee.worksFor = (Department)cmhDepartment.Element.AsObject; |
同样,对于
Add
Candidate按钮:
Candidate theCandidate = new Candidate(EcoSpace);
// Set the department of this candidate to be the object selected
// by dgDepartment
theCandidate.mightWorkFor = (Department)cmhDepartment.Element.AsObject; |
请注意此处一对有用的功能。其中一个就是用于 UML 图中关联的名称(worksFor、mightWorkFor)是以 Employee 和
Candidate 对象的“属性”出现的。这些属性各自返回自己真正的 .NET 对象。这使得您可以使用诸如
theCandidate.mightWorkFor.Location
的表达式来找到候选者(candidate)可工作的位置。这可用于对
DB2® 使用 SELECT 查询的地方,只是此处您可以直接使用本机 C# 对象类型。
编译并运行应用程序。
图 3:按部门(department)筛选的应用程序
在按下 Add Employee 或 Add Candidate 按钮之前,请您确保已输入了一个部门(department),否则您调用
cmhDepartment.Element.AsObject 时将返回空的对象引用。更为复杂的应用程序版本很可能会检查这一点并使按钮变成灰色。
当您添加雇员(employee)或候选者(candidate)时,您将看到在相应的数据网格中出现了新的一行。试试输入两、三行数据,然后切换至另一部门(department),并输入更多行数据。您应该看到当您更改部门(department)行时,Employee
和 Candidate 网格也会随之自动更改 —— 这是由 CurrencyManagerHandle 组件自动级联的一个事件。
微调界面
您将注意到 Employee 和 Candidate 网格中分别为 worksFor 和 mightWorksFor 属性设置了一列。此处所显示的数据仅为该对象的名称
—— Department 对象没有默认属性,所以只能获得其对象名。
要删除该列,就请关闭应用程序并返回主 WinForm 的窗体设计器。我们所使用的 DataGrid 与任何其他的 .NET 应用程序是相同的,所以您可以设置属性以显式地选择您所需要的列。
选择
dgEmployee
网格,并且选择
TableStyles
属性编辑器(位于 Data 下面)。这将显示
DataGridTableStyle
Collection 编辑器。它在默认情况下为空:按
Add
按钮来基于 .NET 默认值添加一个新的
TableStyle。
您可以在这里修改网格的外观 —— 字体、颜色、是否显示交替背景线,等等。.NET DataGrid 支持一次显示多个表,但我们在这里只使用一个表,所以无需再添加任何其他东西。
您将需要为调整列而设置的特殊属性就是
GridColumnStyles,位于底部
Misc
之下。单击属性编辑器后,您将看到出现一个二级嵌套的 collection 编辑器:
DataGridColumnStyle
编辑器。
这里,我们可以显式地选择数据网格中每列的属性。为第一列添加
DataGridTextBoxColumn,将其
HeaderText
属性设置为
Name
以及将
MappingName
属性设置为
Name。除了
worksFor 列,对所有其他列重复同样的操作。
选择
ColumnStyle
编辑器,然后选择
TableStyle
编辑器。现在,您的网格看上去要整洁得多了。这里,我修改了列标题(column heading),使之留出间隔以更加易读:
图 4:带有定制列的应用程序
添加附加窗体
但是如果我们希望通过一个常规的 Windows 对话框让用户选择雇员(employee)所工作的部门(department),又该如何做呢?在这个最后的步骤中,您将看到如何在应用程序使用另一个
WinForm,以作为可编辑 Employee 对象的对话框。
默认情况下,所创建的默认的空 ECO 应用程序包括一个 EcoWinForm。切换至该窗体后,您将看到它已经包含了 RootHandle
和 ExpressionHandle 组件。我们会将其用作我们的对话框,首先就是将该窗体保存为
GetEmployee.cs。将该窗体命名为
ewfEditEmployee
并将其标题(title)设置为
Enter
Employee Details。
完成该工作后,选择根句柄
rhRoot
并通过将其
EcoSpaceType
属性设置为下拉框中的
HRApp.HRAppEcoSpace
将其指向用于该应用程序的模型。
请注意,这一举动使得该窗体上的 ECO 组件可获得模型中的对象。如果您已习惯 Delphi 中数据模块的概念,并在 C#Builder 中想念该功能,那么通过
ECOWinForm 可取得许多与集中式关联数据相同的功能。
本例中,您将仅用该对话框来操作 Employee 对象。并非构造一个表达式句柄且在每次构造 OCL 表达式时引用它,我们可以直接为根句柄设置默认类型。通过
Type
Name Selector属性编辑器将
StaticValueTypeName
设置为类
Employee:
图 5:针对静态值类型的 Type Name Selector
完成该工作后,根句柄本身将期望其 Element 属性为 Employee 对象。
现在,我们可以构造用户界面。给窗体添加七个
Label
组件、六个
TextBox
组件、一个
ComboBox
和一个
Button,并按下图放置:
图 6:Employee 对话框的基本设计
设置每个
TextBox
组件的
DataBindings
属性,以使
Text
指向合适的
DataSource:
图 7:为 textBox1 选择 Name 属性
现在,该窗体将自动显示由窗体根句柄引用的当前的 Employee 对象。
以该方式显示单条记录的好处是,您现在易于用部门(department)名称填充组合框。为此,您将需要使用已位于该窗体上的表达式句柄。选择它并将其重新命名为
ehDepartment。
按照您的期望来设置属性:
RootHandle: rhRoot (should already be set)
Expression: Department.allInstances |
然后,按照如下内容设置组合框属性:
Name: cbDepartment
DataSource: ehDepartment
DisplayMember: Name
ValueMember: Name |
接下来,为组合框的 SelectedIndexChanged 事件编写下列代码:
// Find out which object was selected
IElement selected =
(ehDepartment.Element as IObjectList)[cbDepartment.SelectedIndex];
if (selected == null) return;
Department selDepartment = (Department)selected.AsObject;
// Extract the employee instance from rhRoot
Employee thisEmployee = (Employee)rhRoot.Element.AsObject;
thisEmployee.worksFor = selDepartment; |
请注意默认情况下,EcoWinForm 不包括对程序集 Borland.Eco.ObjectRepresentation 的引用,该程序集中定义了
IElement。在声明名称空间之前,您将需要在源代码单元顶部添加
下面这行代码:
using Borland.Eco.ObjectRepresentation;
现在,组合框应该更新了通过根句柄所引用的 Employee 对象的 worksFor 属性值。
切回至窗体设计,双击
Close
按钮,并输入以下行:
this.Close();
剩下的就是从主应用程序建立一条路径以加载 GetEmployee 窗体。一种较为简洁的方式就重载 EcoWinForm 本身的构造函数。
如果您向上滚动接近源代码单元
GetEmployee.cs
的开头,您将看到窗体的默认构造函数:它是一个公共函数
ewfEditEmployee(HRAppEcoSpace
EcoSpace)。我们复制该函数并且添加
Employee
对象,并且希望将该对象编辑为一个附加参数。
复制该函数并添加附加参数;在
AutoContainer
行之前,设置根句柄元素。整个函数将如下所示:
public ewfEditEmployee(HRAppEcoSpace ecoSpace, Employee ourEmployee)
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Set EcoSpace (actually stores it in rhRoot.EcoSpace)
this.EcoSpace = ecoSpace;
// Set root handle to employee object passed in constructor
rhRoot.SetElement(ourEmployee.AsIObject());
// Hook up AutoContainer provider
new AutoContainerProvider(this, EcoSpace);
}
|
现在,我们可以返回到主窗体
WinForm.cs
了。在
Click
处理程序中为
Add
Employee按钮添加下列两行:
ewfEditEmployee editForm = new ewfEditEmployee(EcoSpace, theEmployee);
editForm.ShowDialog();
|
现在,您已经构建了一个可以直接编辑 Employee 对象的窗体。
编译并且运行该应用程序。现在当您添加一个雇员(employee)时,就将显示新的对话框。
当您输入数据时,您将看到它会出现在网格中的记录下面,这表明您正在直接编辑对象。虽然在存储到 DB2 之前,它在 EcoSpace 中是缓冲在本地内存中的,但是在这种情况下,您或许更愿意使用一个“临时”Employee
对象。
结束语
ECO 应用程序的开发原则完全使用了面向对象的概念。这些最初对于 DB2 开发人员来说看上去有些陌生,但是 ECO“句柄”方法以一种比通过更为常规的关系数据库技术更加高效的方式提供了一种极其灵活的方法来构建应用程序。
您所构建的小型应用程序说明了 ECO 的主要概念 —— 如何设计模型,如何将数据持久存储到 DB2,以及如何构建相当复杂的用户界面。DB2
很适合作为 ECO 应用程序的后备存储器,因为它们通常需要在一个操作中检索或存储极为大量的数据,而这正是 DB2 的出众之处。
关于作者  | |  | Jeremy McGee 最初在 Commodore PET 上用 BASIC 编写应用程序。对于在早期的苹果机上用冰箱般大小的激光打印机编排书那么长的出版物,他至今记忆犹新。从那时起,他担任过
DEC VAX 的系统管理员、Borland Paradox 的技术支持工程师,以及启动欧洲 Borland Delphi 的小组成员之一。目前,Jeremy
在英国的南安普敦经营一家顾问公司. |
对本文的评价
|