3.8 TField对象的SetText和GetText事件处理函数
在许多数据库设计中,分析师都会使用键值来代表特定的信息,例如使用ID值来代表部门代号,使用书籍号码来代表书名等。这些技巧有几个目的,其中最重要的目的是在数据表中维持唯一的特性,以方便查寻特定的数据。其他的则可能是为了数据库范式化的目的(不是所有的分析师都知道数据库范式化),也有可能是为了节省数据库储存空间等。
虽然上述的设计很正确,但是在用户使用应用程序时却可能不希望看到对用户无意义的键值代号,而是希望看到完整的信息。例如用户可能希望看到完整的书籍名称,而不是书籍代号,除非这个用户是书商,能够熟记每一个书籍代号代表的书籍,因此,解决这个数据库中信息转换的工作是许多数据库应用程序都需要处理的。
要解决这个问题有许多不同的方法,Delphi的TField对象也提供了两个相关的事件处理函数SetText以及GetText来帮助程序员解决这个问题。GetText事件被触发的时机是当客户端需要取得字段的数值时,因此,键值的字段程序员就可以在这个事件处理函数中使用键值到其他数据表中查寻这个键值代表的完整信息,再把此完整信息回传当成字段的数值即可。下面是GetText的声明原型:
type TFieldGetTextEvent = procedure(Sender: TField; var Text: String; DisplayText: Boolean) of object;
TFieldGetTextEvent的第一个参数Sender便是目前要取得数值的字段,第二个参数Text是声明为var类型的字符串参数,程序员必须在这个参数中指定代表此字段的数值,最后一个参数DisplayText则是用来代表这个字段是使用来显示的或是允许用户修改的。
如果程序员使用了GetText事件处理函数转换字段的代表数值,那么程序员通常就需要搭配SetText事件处理函数。因为当GetText改变字段代表的数值之后,用户如果在客户端修改了这个字段的数据,并且在用户把数据更新回数据来源中时,程序员必须把数据转换回原始的格式,再更新到数据来源中。而进行这个数据回转的好地方就是SetText事件处理函数。
SetText事件的声明原型如下:
type TFieldSetTextEvent = procedure(Sender:
TField; const Text: String) of
object;
其中的第一个参数Sender是要更新数据的字段,而Text则是客户端传递来的代表此字段的数值,程序员可以使用参数Text的数值恢复成正确的数据,再把这个正确的数据指定给Sender的Value特性值。
现在让我们使用一个范例来说明如何使用GetText和SetText事件处理函数。在D2006HDBP数据库中,BOOKS数据表有一个字段AID代表书籍的作者或是唱片的歌手,当我们使用数据感知控件显示BOOKS数据表时,我们并不希望看到AID的数值,而是希望看到AID代表的人名。因此,这个范例在使用TDBGrid显示BOOKS数据表的内容时,就使用了GetText和SetText事件处理函数来转换AID成人名,并且在用户更改数据时根据人名再转成正确的AID数值,并更新回BOOKS数据表之中。
首先建立一个Delphi项目,在数据模块中放入如下的dbExpress控件以连接D2006HDBP数据库(见图3-27)。

图3-27 使用dbExpress连接D2006HDBP
TSQLConnection:
|
特性名称 |
设定特性值 |
|
Name |
D2006HDBP |
|
Database |
F:\My Books\dbExpress 2006\D2006HPDBP\Data\d2006hpdbp.gdb |
TSimpleDataSet:
|
特性名称 |
设定特性值 |
|
Name |
sdsBooks |
|
DBConnection |
D2006HDBP |
|
DataSet\CommandText |
select * from BOOKS |
TSQLDataSet:
|
特性名称 |
设定特性值 |
|
Name |
sqldsPerformersByID |
|
DBConnection |
D2006HDBP |
|
CommandText |
select NAME from PERFORMERS where AID = :AID |
TSQLDataSet:
|
特性名称 |
设定特性值 |
|
Name |
sqldsPerformersByName |
|
DBConnection |
D2006HDBP |
|
CommandText |
select AID from PERFORMERS where NAME = :NAME |
TSQLDataSet:
|
特性名称 |
设定特性值 |
|
Name |
sqldsGeneral |
|
DBConnection |
D2006HDBP |
接着启动sdsBooks的字段编辑器加入所有的字段,并且使用对象查看器为AID字段定义GetText和SetText,如图3-28所示。

图3-28 启动sdsBooks的字段编辑器,加入所有字段,再设定AID字段的GetText和SetText事件处理函数
现在我们希望使用TDBGrid显示BOOKS数据表的内容,并且希望AID字段能够显示一个下拉框允许用户在更改此字段的数据时能够使用选择人名的方式。因此在主窗体中加入一个TDBGrid控件,双击TDBGrid启动字段编辑器,加入所有的字段如图3-29所示。由于我们希望在此字段能够让用户选择人名,因此我们只需要把人名的数据加入到图3-29中AID的PickList特性值之中即可。

图3-29 启动sdsBooks的字段编辑器,加入所有字段,再设定AID字段的GetText和SetText事件处理函数
首先我们实现数据模块中AID字段的GetText和SetText字段的事件处理函数。下面即是相关的实现程序代码:
procedure TdmSetGetTextDemo.sdsBooksAIDGetText(Sender: TField;
var Text: String; DisplayText: Boolean);
begin
try
Self.sqldsPerformersByID.ParamByName('AID').Value := Sender.Value;
Self.sqldsPerformersByID.Active := True;
Text := Self.sqldsPerformersByID.Fields[0].AsString;
finally
Self.sqldsPerformersByID.Active := False;
end;
end;
procedure TdmSetGetTextDemo.DataModuleCreate(Sender: TObject);
begin
Self.sqldsPerformersByID.Prepared := True;
Self.sqldsPerformersByName.Prepared := True;
end;
procedure TdmSetGetTextDemo.DataModuleDestroy(Sender: TObject);
begin
Self.sqldsPerformersByID.Active := False;
Self.sqldsPerformersByID.Prepared := False;
Self.sqldsPerformersByName.Active := False;
Self.sqldsPerformersByName.Prepared := False;
Self.D2006HDBP.Connected := False;
end;
procedure TdmSetGetTextDemo.sdsBooksAIDSetText(Sender: TField;
const Text: String);
begin
try
Self.sqldsPerformersByName.ParamByName('NAME').Value := Text;
Self.sqldsPerformersByName.Active := True;
Sender.Value := Self.sqldsPerformersByName.Fields[0].Value;
finally
Self.sqldsPerformersByName.Active := False;
end;
end;
在sdsBooksAIDGetText事件处理函数中,我们根据AID字段目前的数值到PERFORMERS数据表中查寻代表此ID的人名信息,再把找到的人名指定给第二个参数Text。而sdsBooksAIDSetText事件处理函数则是执行相反的工作,它根据传递来的人名数据,再回到PERFORMERS数据表中查寻此人名的AID数值,最后再指定给代表字段对象的Sender参数的Value特性值。
GetText和SetText事件处理函数执行的次数非常地频繁,为了增加执行效率,我们分别在数据模块的OnCreate事件处理函数中先Prepare sqldsPerformersByID和sqldsPerformersByName控件要执行的SQL语句,并且在数据模块的OnDestroy事件处理函数中Unprepare,以释放数据来源中使用的资源。
最后再回到范例程序的主窗体中,其中主要的工作就是显示TDBGrid时在AID字段的PickList中填入目前在PERFORMERS数据表中所有的人名信息,这个工作是在范例主窗体的OnShow事件处理函数中调用FillPerformerInfos函数完成的。而FillPerformerInfos函数则是使用数据模块中的sqldsGeneral控件执行SQL语句,从PERFORMERS数据表中取得所有的人名数据,再填入到TDBGrid中AID字段的PickList特性值之中。
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
if (dmSetGetTextDemo.sdsBooks.ChangeCount > 0) then
dmSetGetTextDemo.sdsBooks.ApplyUpdates(0);
end;
procedure TForm1.FillPerformerInfos;
begin
if (Self.DBGrid1.Columns.Items[7].PickList.Count = 0) then
begin
dmSetGetTextDemo.sqldsGeneral.Active := False;
dmSetGetTextDemo.sqldsGeneral.CommandText := 'select distinct NAME from PERFORMERS';
try
dmSetGetTextDemo.sqldsGeneral.Active := True;
while not dmSetGetTextDemo.sqldsGeneral.Eof do
begin
Self.DBGrid1.Columns.Items[7].PickList.Add(dmSetGetTextDemo.sqldsGeneral.Fields[0].AsString);
dmSetGetTextDemo.sqldsGeneral.Next;
end;
finally
dmSetGetTextDemo.sqldsGeneral.Active := False;
end;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
FillPerformerInfos;
end;
最后我们编译此范例并且执行它,那么用户便可以看到类似如图3-30所示的画面,请注意此时AID字段显示的不再是代表人名的ID数值,而是完整的人名数据,而且用户可以使用下拉框的方式来进行数据更改的工作。如果用户修改了AID字段中人名的数据并且点击主窗体中的ApplyUpdates按钮,那么数据模块中的SetText事件处理函数便会被触发,再把人名回转成正确的ID数值并更新回BOOKS数据表的AID字段之中。

图3-30 范例程序执行的画面
当然,本范例只是为了展示如何使用GetText和SetText这两个事件处理函数,对这个范例来说,由于PERFORMERS数据表中的数据并不多,因此对于这种需要查询的数据表而言,如果数据量不多的话,那么程序员可以使用一个TSimpleDataSet/TClientDataSet把数据先存取到客户端,再于GetText和SetText事件处理函数中直接使用TSimpleDataSet/TClientDataSet的Locate方法来进行数据转换的工作,这样可能会比较有效率。






