怎么样通过DB2表为Delphi产生动态的数据输入窗体?
怎么样通过DB2表为Delphi产生动态的数据输入窗体?
本文检查了 IBM DB2 Universal Database 的元数据,以便动态构建视图和窗体,其中包括如何动态生成新的 CLX 窗体,以及将窗体流化(stream)为 Delphi .pas 和 .xfm 文件,并准备将它们添加到 Kylix 和 Delphi CLX 项目中。
简介
在 上一篇文章中,我检查了 IBM® DB2® Universal Database™(UDB)的元数据,以便动态构建视图和窗体。我使用了 Linux 上的 Borland® Kylix™ 3 和 dbExpress 数据访问驱动程序,来分析 IBM DB2 UDB 数据库表、字段(名称和类型),允许用户选择特定的表,来回切换应该显示的字段,以及在数据网格(datagrid)和单个数据感知控件中动态查看输出。
这次,我们将扩展该方法,以允许用户指定更加复杂的查询(例如 JOIN)。作为附加步骤,我们将产生一个单独的窗体,并以 .pas 和 .xfm 源格式保存它,因此,可以将结果窗体添加至 Borland Delphi™ 或 Kylix 项目中。这类似于原来的基于 BDE(且仅用于 Windows)的数据库-窗体向导,但这次产生的是一个跨平台的 CLX 窗体。
从 Linux 进行迁移
上次,我完成了一个在 Linux 上运行的 Kylix 3 项目。这次,我首先需要将该项目迁移到 Windows 上,并在那里进行扩展(用以说明该项目和由此生成的 CLX 窗体是跨平台的,既可以用于 Linux 上的 Kylix,也可用于 Windows 上的 Delphi)。
将该项目从 Kylix 迁移到 Delphi 仅涉及一项更改:LibraryName 和 VendorLib 的 TSQLConnection 属性在 Linux 上被设置为 libsqldb2.so 和 libdb2.so,但在 Windows 上,该属性必须设为 dbexpdb2.dll 和 db2cli.dll。关于如何创建跨平台项目的指导,请参阅 大转变:利用 Kylix 3 从 Windows 迁移到 Linux。
添加 SQL 功能
一旦可以在 Delphi 7 中重新打开该项目,您就可以对页面控制的 Meta Data 选项卡进行一些增强。具体地说,您需要允许用户输入 SQL 语句,而不是只能从可用表的列表中选择表名。图 1 展示了新的用户界面(显示了 EMP_PHOTO 表的字段),您可以使用左下方的 memo 字段输入 SQL 语句。
图 1. 用于 DB2 的新动态窗体
就在 TMemo 控件的正上方,我放置了文本 SELECT * FROM,作为小小的一个提示:用户只需指定表名,以及下面可选的 where子句。可以用代码来构造 SQL 语句,检索元数据(metadata)来获取字段名,并将这些字段名置于 TCheckListBox 中,该代码如下所示:
|
作为一个实际示例,我输入了一条 SQL 语句,以连接 EMPLOYEE 和 EMP_PHOTO 表,该操作是基于 EMPNO 字段的。因为已经给定了 SELECT * FROM,所以我只需要在查询 memo 字段中输入 EMP_PHOTO, EMPLOYEE WHERE EMP_PHOTO.EMPNO = EMPLOYEE.EMPNO,然后启用 Select fields from query复选框,这将执行以上代码,产生 TCheckListBox 中的字段列表。
图 2. 从查询中选择字段
请注意,EMPNO 字段出现了两次:一次是在 EMP_PHOTO 表中(称作 EMPNO),一次是在 EMPLOYEE 表中(然后自动称作 EMPNO_1)。
在上面的实例中,通过左下方所指定的 where子句,我从查询中选择了 PICTURE、FIRSTNME、LASTNAME、PHONENO、HIREDATE 和 SEX 字段。
为了给 DBGrid 和其他数据感知控件提供真正的数据,您需要添加一些代码,以获取表名(从 TListBox 中)或 where子句(从 TMemo 控件中)。这项决策是基于复选框的启用情况的,其编码如下:
|
这确保您可以使用带有表名的 TListBox,或使用带有 SQL where 子句的 TMemo ,来选择字段并为视图产生数据。
添加更多的控件
您在图 2 中选择的字段是 PICTURE 字段。上次,我在由 TDBMemo 表示的 Memo 字段之间进行了区分,虽然其他所有字段都是由 TDBEdit 表示的。对于可以是 ftGraphic 或 ftBlob(本实例中)的 image 字段,您可以使用 TDBImage 控件。请注意,ftBlob 的情况并不是百分之百确定的:大多数 BLOB 字段只包含二进制数据,而不是总包含图像。但是对于本例,ftBlob PICTURE 字段包含图像(以不同的格式,如 bmp 或 gif)。
必须添加一些附加代码,以检测字段类型,并通过动态创建 TDBImage 对此作出响应,这些附加代码片断如下所示:
|
在您选择了 PICTURE、FIRSTNME、LASTNAME、PHONENO、HIREDATE 和 SEX 字段的示例中,该代码的结果如图 3 所示。
图 3. 动态数据控件预览
请注意,这里还有一个附加的按钮,在这里,将用它在新的 CLX 窗体上从该 tabsheet 重新创建这些控件。该窗体将被流化为 Delphi .pas 源文件和 .xfm 窗体文件;并准备将它们添加至 Kylix 或 Delphi CLX 项目中。
设计新的窗体框架
虽然在 tabsheet 中查看数据效果极佳(如图 3 中),但是在新的窗体(不同于原先那个您指定了表名或查询,并选择了要使用的字段名的窗体)中查看控件将具有更强大的功能。您必须重新创建 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件,这意味着您可以从一个几乎为空的框架窗体开始,准备创建到 DB2 UDB 数据库新的 dbExpress 连接,输入 SQL 语句,并放置数据感知控件,以显示数据。
通过 Delphi 7,我设计了一个新的 CLX 窗体,该窗体包含 TSQLConnection、TSQLDataSet、TDataSetProvider、TClientDataSet、TdataSource 和 TDBNavigator 控件;其布局如图 4 中所示。像您以前多次做过的那样,将它们都关联起来,但是此时还没有任何数据感知控件。
图 4. 设计时的新窗体模板
现在,应该为图 3 中首次出现的 Generate Code 按钮实现 OnClick 事件处理程序了。
生成新的窗体内容
Generate Code 按钮将创建新 CLX 框架窗体的一个实例,并动态添加 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件(使用与原来窗体相同的属性值)。用来创建新的 CLX 窗体并克隆将放置在新 CLX 窗体上的控件的代码如下所示:
|
在该事件处理程序的结尾,通过调用 ShowModal 显示了 NewForm,其结果如图 5 所示。因为就在显示该窗体之前激活了 TClientDataSet(关闭该窗体之时再取消激活状态),所以该图将显示带有数据的新窗体。
图 5. 运行时的新窗体
在显示了该窗体之后,您可以创建一个 Delphi .xfm 文件,其中包含窗体的流化(streaming)信息、其上所有的组件及其属性信息。对 WriteComponentResFile 的调用将为该窗体创建一个二进制的源文件。该文件可用于 Kylix 或 Delphi CLX 项目,但是还需要相应的 .pas 文件,其中包含窗体上每个组件的源代码声明。
生成相关的源代码
最后一步(也集成在“Generate Code”OnClick 事件处理程序中)是创建包含组件声明的相应的 .pas 文件。这是一个由三个阶段组成的过程。首先,您要编写该 .pas 文件的第一部分,然后动态添加单个 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件,最后,添加该 .pas 文件的最后一部分。
procedure TDBWizForm.btnWizardClick(Sender: TObject); var f: System.Text; LabelNr,EditNr,MemoNr,ImageNr,i: Integer; begin System.Assign(f,UnitName+'.pas'); Rewrite(f); writeln(f,'unit ',UnitName,';'); writeln(f,'interface'); writeln(f,'uses'); writeln(f,' SysUtils, Types, Classes, Variants, QTypes, QGraphics, QControls, QForms,'); writeln(f,' QDialogs, QStdCtrls, DBXpress, QCheckLst, DB, SqlExpr, QComCtrls, FMTBcd,'); writeln(f,' DBClient, Provider, QGrids, QDBGrids, QDBCtrls, QExtCtrls, QMask;'); writeln(f); writeln(f,'type'); writeln(f,' TNewForm = class(TForm)'); writeln(f,' SQLConnection1: TSQLConnection;'); writeln(f,' SQLDataSet1: TSQLDataSet;'); writeln(f,' DataSetProvider1: TDataSetProvider;'); writeln(f,' ClientDataSet1: TClientDataSet;'); writeln(f,' DataSource1: TDataSource;'); writeln(f,' DBNavigator1: TDBNavigator;'); LabelNr := 0; EditNr := 0; MemoNr := 0; ImageNr := 0; for i:=0 to Pred(ThisForm.ComponentCount) do begin if (ThisForm.Components[i] is TLabel) then begin Inc(LabelNr); Name := Format('Label%d',[LabelNr]); writeln(f,' ',Name,': TLabel;'); end else if (Components[i] is TDBEdit) then begin Inc(EditNr); Name := Format('Edit%d',[EditNr]); writeln(f,' ',Name,': TDBEdit;'); end else if (Components[i] is TDBMemo) then begin Inc(MemoNr); Name := Format('Memo%d',[MemoNr]); writeln(f,' ',Name,': TDBMemo;'); end else if (Components[i] is TDBImage) then begin Inc(ImageNr); Name := Format('Image%d',[ImageNr]); writeln(f,' ',Name,': TDBImage;'); end end; writeln(f,' procedure FormCreate(Sender: TObject);'); writeln(f,' private'); writeln(f,' { Private declarations }'); writeln(f,' public'); writeln(f,' { Public declarations }'); writeln(f,' end;'); writeln(f); writeln(f,'var'); writeln(f,' NewForm1: TNewForm;'); writeln(f); writeln(f,'implementation'); writeln(f); writeln(f,'{$R *.xfm}'); writeln(f); writeln(f,'procedure TNewForm.FormCreate(Sender: TObject);'); writeln(f,'begin'); writeln(f,' try'); writeln(f,' ClientDataSet1.Active := True'); writeln(f,' except'); writeln(f,' end'); writeln(f,'end;'); writeln(f); writeln(f,'end.'); System.Close(f); end; |
完整的源代码集成位于事件处理程序之中,可通过下载获得。
使用结果
其结果就是一个 .pas 以及相应的 .xfm 文件,可以将它们用于 Linux 上的 Kylix 以及 Windows 上的 Delphi CLX 项目中。例如,目前给出了给定演示的结果窗体,如图 6 中所示,该图中显示了 Delphi 7 在设计时显示的结果窗体。该窗体与初始的框架窗体一样,只是添加了 TLabel、TDBImage 和 TDBEdit 控件,事实上,Height 已经被修改,以显示所有控件。
图 6. 设计时生成的新窗体
正如我上次提到的,所生成的窗体不包含调用 TClientDataSet 的 ApplyUpdates 方法(用以将更新送回数据库)或 UndoLastChange,以及其他 Undo 方法的按钮。这将作为练习留给读者来完成。作为一点小小的一个提示,我建议在 NewForm 中将这些按钮添加至框架窗体,因此,您所做的修改将用于所有新生成的窗体中。那样的话,就可以将 NewForm 视作一个基类,用于所有动态创建的数据库窗体。
结束语
在这篇以及上一篇文章中,都检查了 IBM DB2 Universal Database 元数据,以便动态构建视图和窗体。您已经使用了带有 dbExpress 的 Linux 上的 Borland Kylix 3,以及 Windows 上的 Delphi 7,来分析 DB2 UDB 数据表、字段(名称和类型),允许用户选择特定的表或指定 SQL 语句的 join/where 子句,来回切换应该显示的字段,以及在数据网格(datagrid)和单个数据感知控件中动态查看输出。
最后一步包括动态生成新的 CLX 窗体,以及将该窗体流化为 Delphi .pas 和 .xfm 文件,并准备将它们添加到 Kylix 和 Delphi CLX 项目中(为 DB2 UDB 数据库表产生我们自己定制的数据库窗体向导)。