第十二章 使用数据控件
在数据库应用程序中,数据控件是经常要用到的。与前面介绍的数据集构件如TTable、TQuery、TStoredProc、TClientDataSet不同的是,数据控件都是可视的。也就是说,如果修改了这些构件的属性,能在窗体上马上反映出来。如果这些构件的Enabled属性设为True并且数据集的Active属性也设为True,在设计期就可以看到数据。
12.1 Delphi 4中有哪些数据控件
在Delphi 4的IDE中,数据控件都位于构件选项板的“Data Controls”页上。下面简单介绍这些数据控件:
.TDBGrid以行和列组成的栅格显示数据。
.TDBNavigator用于对整个数据库的记录进行导航,如向前翻一个记录、向后翻一个记录、翻到第一个记录、翻到最后一个记录等。
.TDBText把字段的内容作为标签显示。
.TDBEdit以编辑框的形式显示一个字段的内容。
.TDBMemo以多行文本编辑器的形式显示备注字段的内容。
.TDBImage用于显示数据库中的图像字段。
.TDBListBox以列表框的形式显示数据。
.TDBComboBox以组合框的形式显示数据。
.TDBCheckBox以复选框的形式显示数据。
.TDBRadioGroup以单选框分组框的形式显示数据。
.TDBLookupListBox以列表框的形式显示这个表的Lookup表的数据。
.TDBLookupComboBox以组合框的形式显示这个表的Lookup表的数据。
.TDBRichEdit以RTF格式显示备注字段的内容。
.TDBCtrlGrid与TDBGrid相似,但每一个单元都可以分别设置属性。
.TDBChart以图表的形式显示数据库的数据,用法与TChart相似。
上述数据控件在设计期具有数据感知的特点。如果正确设置了DataSource属性指定了一个数据源,马上就能看到数据,而不需要编译和运行程序。
12.2 数据控件的基本用法
这一节介绍数据控件的基本用法,包括怎样指定一个数据源、怎样编辑和更新数据、怎样禁止和允许数据刷新。
12.2.1 指定一个数据源
数据控件必须通过TDataSource构件连接数据集。TDataSource构件扮演的角色实际上就是数据控件与数据集之间的桥梁。
首先,把一个数据集构件放到窗体或数据模块上,设置它的DatabaseName属性指定要访问的数据库,设置它的TableName属性指定要访问的表。
接着,把一个TDataSource构件放到窗体或数据模块上,设置它的DataSet属性指定数据集。
然后,把一个数据控件放到窗体上,设置它的DataSource属性指定TDataSource构件,而这个TDataSource构件的DataSet属性已经指定了一个数据集。
最后,设置数据控件的DataField属性指定要显示的字段。不过,对于TDBGrid、TDBCtrlGrid和TDBNavigator构件来说,不需要设置DataField属性,因为这几个控件是以整个数据集为工作内容的。
12.2.2 编辑和更新数据
除了TDBNavigator构件外,其他数据控件都是用来显示和编辑数据的。这里要介绍怎样编辑数据。
要使用户能编辑数据,数据集必须进入dsEdit状态。如果TDataSource的AutoEdit属性设为False,用户不能直接编辑数据,除非程序调用Edit函数。
要使用户能够在数据控件中修改数据,必须把数据控件的ReadOnly属性设为False。如果ReadOnly属性设为True,数据控件中显示的数据就是只读的。
一般情况下,TDataSource构件的Enabled属性设为True。如果这个属性设为False,数据控件就无法显示数据,更不能修改数据。
如果数据集构件的ReadOnly属性设为True,数据集就是只读的,用户在数据控件中所作的修改不能写到数据集中。
除了TDBGrid构件外,当用户修改了一个字段的值,还需要把输入焦点移走,新的数据才写到数据集中。在移走输入焦点之前,用户随时可以按ESC键取消修改。
在TDBGrid构件建立的栅格中,当用户修改了一个字段的值,还需要把输入焦点移到另一条记录上,新的数据才写到数据集中。
12.2.3 禁止和允许数据刷新
当程序正在遍历整个数据集或者搜索一个特定的记录时,应当暂时禁止数据控件刷新数据,这样能加快遍历或搜索的速度,防止屏幕总是在闪烁。
调用数据集的DisableControls可以暂时禁止连接这个数据集的数据控件刷新数据。DisableControls函数通常在循环操作前调用。等循环结束后,程序应当立即调用数据集构件的EnableControls函数重新允许刷新数据。
为了确保最后总是能恢复刷新,建议采用Try...Finally结构。这样,即使在循环中出现异常,也可以保证总能调用EnableControls。
下面的代码演示了怎样调用DisableControls和EnableControls函数:
CustTable.DisableControls;
Try
CustTable.First;
While not CustTable.EOF Do
Begin
...
CustTable.Next;
End;
Finally
CustTable.EnableControls;
End;
12.2.4 手动刷新数据
调用数据集的Refresh可以读取数据集中最新的数据并刷新数据控件,这个功能在多用户环境尤其有用,因为其他用户有可能已改变了数据集中的数据。
有时候,调用Refresh可能会导致意想不到的结果。例如,如果另一个用户已经删除了一条记录,调用Refresh后,这条记录将从数据控件中消失。
12.3 显示单个字段的数据控件
有的数据控件以数据库的一个或几个字段作为工作内容,如TDBText和TDBEdit,而有的数据控件以整个数据集为工作内容,如TDBGrid和TDBNavigator。
显示单个字段的数据控件往往是从一个标准的Windows控件演化而来的,例如,TDBEdit构件就可以认为是TEdit的数据感知版本。
图12.1 用TDBText构件把数据作为标签显示
12.3.1 把数据作为标签显示
TDBText构件是一个只读的数据控件,它非常类似于TLabel构件和TStaticText构件。TDBText构件能够把数据作为标签显示,用来标注其他控件。例如,可以用一个TDBText构件在鱼的图片下显示鱼的名称(Common_Name字段),如图12.1所示。
TDBText构件需要指定一个字段。当用户使用导航器或其他手段浏览记录时,TDBText构件显示的数据将自动变化,因为TDBText构件总是显示当前记录的数据。例如,在图12.1中,当用户用鼠标单击栅格的滚杆两端的箭头时,鱼的图片将自动变化,鱼的名称也相应改变,这一切都不需要编程。
TDBText构件的AutoSize属性一般要设为True,这是因为字段的内容长度可能是不同的。如果AutoSize属性设为False,有些较长的内容可能会被截断。
12.3.2 显示和编辑数据
TDBText构件只能显示数据,不能编辑数据。要既能显示数据,又能编辑数据,就要用到TDBEdit构件。TDBEdit可以认为是TEdit的数据感知(Data-Aware)版本。
例如,有一个TDataSource构件叫CustomersSource,它的DataSet属性指向一个TTable构件叫CustomersTable。把一个TDBEdit构件放在窗体上,其DataSource属性设为CustomersSource,把它的DataField属性设为CustNo。这个TDBEdit构件马上就能显示CustNo字段的值。用户可以在编辑框中键入新的值。
12.3.3 显示和编辑多行文本
TDBMemo构件是TMemo构件的数据感知版本,可以显示dBASE和Paradox数据库中备注字段的内容。
与TDBEdit不同的是,TDBMemo能够以多行的形式显示文本,同时也允许用户键入多行文本。
默认情况下,TDBMemo允许用户修改它显示的文本。如果不想让用户修改文本,只要把ReadOnly属性设为True即可。要允许用户在文本中插入一个制表符,应当把WantTabs属性设为True,否则,当用户按下Tab键,将把输入焦点移走,而不是插入制表符。
要限制用户最多可输入的字符数,可以设置MaxLength属性。如果这个属性设为0,表示没有限制。
此外,ScrollBars属性可以设置要不要加上滚动栏,WordWrap属性可以设置是否允许自动绕回,Alignment属性可以设置文本的对齐方式。
在运行期,您可以调用CutToClipboard和CopyToClipboard函数把选择的文本剪切和复制到剪贴板中,调用PasteFromClipboard能够粘贴剪贴板中的文本。如果AutoDisplay属性设为True,当DataField属性所指定的字段的内容改变了时,TDBMemo构件会自动刷新。如果AutoDisplay属性设为False,TDBMemo 构件上只显示字段名,用户必须双击这个构件或程序调用LoadMemo才能刷新数据。
12.3.4 以RTF格式显示文本
TDBRichEdit构件可以认为是TRichEdit构件的数据感知版本,用于以RTF格式显示BLOB字段中的格式化文本。它的用法类似于TDBMemo构件,也能显示多行文本。
注意:尽管TDBRichEdit构件能够显示RTF格式的文本,并且提供了很强的编辑功能,但是,它本身并没有提供用户界面,应用程序必须设计出相应的用户界面,才能把TDBRichEdit 构件强大的功能发挥出来。
默认情况下,TDBRichEdit构件允许用户键入新的文本。如果不想让用户修改文本,可以把ReadOnly属性设为True。
要允许用户在文本中插入一个制表符,应当把WantTabs属性设为True,否则,当用户按下Tab键,将把输入焦点移走,而不是插入制表符。
要限制用户最多可输入的字符数,可以设置MaxLength属性。如果这个属性设为0,表示没有限制。
如果AutoDisplay属性设为True,当DataField属性所指定的字段的内容改变了时,TDBRichEdit构件会自动刷新。如果AutoDisplay属性设为False,TDBRichEdit构件上只显示字段名,用户必须双击这个构件或程序调用LoadMemo函数才能刷新数据。
12.3.5 显示和编辑图像
TDBImage构件可以认为是TImage构件的数据感知版本,它可以显示BLOB字段的内容。TDBImage构件从数据集中检索了图像后,在本地以DIB格式建立一个副本。
可以调用CutToClipboard或CopyToClipboard函数把图像剪切或复制到剪贴板中,调用 PasteFromClipboard可以从剪贴板中粘贴图像。
如果Stretch属性设为True,图像将自动缩放,以适应TDBImage构件的大小,这样就可能造成图像变形。如果AutoDisplay属性设为True,当DataField属性所指定的字段的内容改变了时,TDBImage构件会自动刷新。如果这个属性设为False,TDBImage构件上只显示字段名,用户必须双击这个构件才能刷新数据,当然也可以调用LoadPicture来刷新数据。
12.4 用列表框和组合框显示和编辑数据
有4个特殊的数据控件可以用列表框和组合框显示和编辑数据,它们可以认为是标准的列表框和组合框的数据感知版本。下面简单介绍这4个数据控件:
.TDBListBox用列表框显示一组数据,让用户从中选择一个值。
.TDBComboBox用组合框显示一组数据,让用户从中选择一个值。
.TDBLookupListBox用列表框显示另一个数据集中的一组数据,让用户从中选择一个值。
.TDBLookupComboBox用组合框显示另一个数据集中的一组数据,让用户从中选择一个值。
12.4.1 TDBListBox
TDBListBox构件能够用列表框显示一组数据,用户可以从中选择一个数据。当用户浏览记录时,程序将自动在列表框中搜索与字段的值匹配的项,如果找到就选择这一项,如图12.2所示。
在列表框中选择匹配的项反过来,当用户在列表框中选择了某个项,程序就自动把当前记录的该字段的值改为列表框中选择的值。当然,要使修改有效还必须调用Post函数。
要在设计期设置列表框中显示的项,可以单击Items属性边上的省略号按钮打开一个字符串列表编辑器,然后输入一些字符串。
如果IntegralHeight属性设为True,列表框的高度将总是项的高度的整数倍。
12.4.2 TDBComboBox
TDBComboBox构件实际上是TComboBox构件的数据感知版本,它能以组合框的形式显示一组数据,让用户从列表中选择一个值或直接输入一个值。
Items属性用于设置列表中要显示的一组数据。在设计期,可以单击Items属性边上的省略号按钮打开一个字符串列表编辑器,然后输入一些字符串。
DropDownCount属性用于设置当用户下拉组合框时不需要加滚动栏就能显示的项的个数,默认是8,表示用户下拉组合框时如果项的个数超过8个才加上滚动栏。如果实际的项数还没有DropDownCount属性指定的值多,下拉的组合框的高度自动缩小。
在Style属性设为csOwnerDrawFixed的情况下,ItemHeight属性用于设置项的高度。
12.4.3 显示另一个数据集中的数据
TDBLookupListBox构件和TDBLookupComboBox构件分别以列表框和组合框的形式显示另一个数据集中的数据。
假设有一个表格叫OrdersTable,其中包含一个CustNo字段,用于表达客户的编号,但OrdersTable表中除了客户的编号外,不包含客户的其他信息。而另一个表格假设叫CustomersTable,除了有CustNo字段外,还有诸如客户的公司名称、地址等信息。
TDBLookupListBox构件可以实现这样的功能,当用户在OrdersTable中浏览记录时,程序首先在CustomersTable中查找与CustNo字段匹配的记录,如果找不到,就从列表中查找与Company字段匹配的字符串;如果找到,就选择这一项,如图12.3所示。
图12.3 TDBLookupListBox构件的用法
当用户在列表中选择了一项,程序就在CustomersTable中查找与字符串匹配的Company字段,如果找到,就用CustNo字段的值替换OrdersTable表中的CustNo字段。
12.5 用复选框处理布尔类型的字段
TDBCheckBox构件可以认为是TCheckBox构件的数据感知版本,用于处理布尔类型的字段。例如,可以用一个复选框来表示客户是否已付帐。
TDBCheckBox构件实际上是把字段的值与预设的两个字符串比较,这两个字符串分别由ValueChecked和ValueUnChecked属性指定。如果字段的值与ValueChecked属性指定的字符串匹配,就选中复选框。如果字段的值与ValueUnchecked属性指定的字符串匹配,就不选中复选框。注意:ValueChecked属性和ValueUnchecked属性所指定的字符串不能相同。
一般情况下,ValueChecked属性设为“True”、“Yes”之类的字符串,但也可以是其他任意的字符串,甚至是一组字符串,彼此之间要用分号隔开,例如:
DBCheckBox1.ValueChecked := 'True;Yes;On';
上述情况下,当字段的值只要与其中一个字符串匹配,就选中复选框。要说明的是,ValueChecked属性指定的字符串是大小写敏感的。
一般情况下,ValueUnchecked属性设为“False”、“No”之类的字符串,但也可以是其他任意的字符串,甚至是一组字符串,彼此之间用分号隔开。
如果字段的值既不与ValueChecked属性指定的字符串匹配,也不与ValueUnchecked属性指定的字符串匹配,复选框就变灰。
12.6 用单选分组框限制字段的值
TDBRadioGroup构件可以认为是TRadioGroup构件的数据感知版本,它可以限制用户从一组数据中选择字段的值。
与TRadioGroup构件一样,首先要设置Items属性指定单选分组框中要显示哪些项。Items属性是一个典型的TStrings对象,每一个字符串对应着单选分组框中的一个按钮。
当用户浏览记录时,如果字段的值与单选分组框中的某个按钮的标签匹配,就选择这个按钮,如图12.4所示。
图12.4 选择与CustNo字段的值匹配的单选框
反过来,当用户在单选分组框中选择一个按钮,程序就用这个按钮的标签赋值给DataField属性指定的字段。
如果不想使按钮的标签与字段的值匹配,可以另外指定其他字符串,这就要用到Values属性。Values属性也是一个TStrings对象,用于指定一组字符串。当用户在单选分组框中选择一个按钮,程序就用Values属性中的一个字符串赋值给DataField属性指定的字段,而不是按钮的标签。
12.7 使用TDBGridTDBGrid构件
以栅格的形式显示和编辑数据集中的数据。它的外观很大程度上取决于下面三个因素:
.一是永久的列对象。
.二是永久的字段对象。
.三是数据集构件的ObjectView属性将影响ADT和数组字段的显示方式。
对于TDBGrid构件来说,最重要的属性是Columns,这是一个TDBGridColumns对象,用于管理一组TColumn对象。在设计期,可以打开一个编辑器建立永久的列对象,然后在对象观察器中设置列对象的属性。
12.7.1动态的列对象
如果TDBGridColumns的State属性设为csDefault,列是动态生成的,列的属性取决于字段的属性。当字段的属性发生变化时,列的属性也跟着变化。
让列动态生成的好处是,可以在运行期动态地选择其他数据集,而不用担心栅格是否适合于显示新的数据集。例如,可以用同一个TDBGrid构件先显示一个Paradox表,再显示查询另一个数据库的结果。
在设计期,无法直接修改动态列对象的属性,只能修改字段对象的属性,从而间接地修改动态列对象的属性。
动态列对象的生存期也取决于字段对象的生存期。如果数据集没有建立永久的字段对象,那么,当数据集关闭时,所有的动态列对象也将消失。
注意:如果在运行期把TDBGridColumns的State属性设为csDefault,将删除所有的列对象,然后根据数据集中的字段对象重建列对象。
12.7.2 永久的列对象
要能够在设计期自定义栅格,就要用到永久的列对象。建立了永久的列对象后,如果TDBGridColumns的State属性设为csCustomized,就可以独立设置每一列的属性。例如,默认情况下,列的标题显示字段的标签即DisplayLabel属性,通过修改TColumnTitle的Caption属性可以重新指定列的标题,而TField的DisplayLabel属性则不会受到影响。
TDBGridColumns的State属性设为csCustomized适合于那些数据集的结构是固定不变的情况。如果需要在运行期切换不同的数据集,就不能把State属性设为csCustomized。
要创建永久的列对象,首先要在窗体上选择TDBGrid构件,然后对象观察器中单击Columns属性边上的省略号按钮将打开如图12.5所示的编辑器。
图12.5 创建永久的列对象
刚开始的时候,这个编辑器是空白的,这是因为,默认情况下,栅格中的列对象都是动态生成的,还没有永久的列对象。
要基于数据集中的每一个字段分别创建一个永久的列对象,可以单击鼠标右键,在弹出的菜单中选择“Add All Fields”命令。
要创建一个独立的永久列对象,可以单击工具栏上的(Add New)按钮。选择这个刚创建的列对象,然后在对象观察器中设置FieldName属性指定一个字段,设置Caption属性指定列的标题。
要删除一个列对象,可以单击工具栏上的(Delete Selected)按钮。如果把永久的列都删掉,栅格反而能显示数据集中所有的字段,这是因为,永久的列删掉以后,Delphi 4会自动把TDBGridColumns的State属性设为csDefault并且动态生成所有的列。要调整列在栅格中显示的顺序,可以用鼠标把列对象前移或后移。
对于永久的列对象来说,它的属性的默认值仍然取自于字段,除非您修改了永久列对象的属性。例如,默认的情况下,列的标题就是字段的DisplayLabel属性。如果修改字段的DisplayLabel属性,列的标题将随之改变。但是,一旦您修改了列对象的Caption属性,列的标题不再与字段的DisplayLabel属性存在联动关系,它们彼此是独立的。
前面讲过,创建一个永久的列对象时,需要设置FieldName属性指定一个字段。不过,您也可以让FieldName属性为空,此时,TColumn对象的Field属性将返回NULL,并且该列在栅格中是空白的。空白的列往往用于用户显示一些自定义的内容,如图像或图表等。
几个列对象的FieldName属性可以设为同一个字段。由此可见,TDBGrid的FieldCount属性可能小于栅格的列数
12.7.3 让用户编辑数据
对于永久的列对象来说,可以提供几种方式让用户编辑数据。一是以组合框的方式显示另一个数据集的数据,二是建立一个静态的列表,三是用省略号按钮打开一个对话框。
要以组合框的方式显示另一个数据集的数据,首先必须在数据集中增加一个Lookup字段,然后创建一个永久的列对象,设置它的FieldName属性指定这个Lookup字段,并且把它的ButtonStyle属性设为cbsAuto。经过上述操作后,栅格的效果如图12.6所示。
图12.6 以组合框的形式显示另一个数据集的数据
要建立一个静态的列表,不必事先增加一个Lookup字段,只要先创建一个永久的列对象,把ButtonStyle属性设为cbsAuto,在对象观察器中单击Picklist属性边上的省略号按钮打开一个字符串列表编辑器,然后依次键入一些字符串。
要用一个对话框让用户选择数据,必须把列对象的ButtonStyle属性设为cbsEllipsis。当程序运行时,该列将出现一个省略号按钮,单击这个省略号按钮将触发OnEditButtonClick事件,在处理这个事件的句柄中可以打开一个对话框,让用户选择数据。对于图像字段来说,当用户单击省略号按钮时,可以显示一个图像。
12.7.4 在设计期设置列对象的属性
列对象的属性决定了数据在栅格中的显示方式。在设计期,可以设置它的下列属性:
.Alignment用于设置数据的对齐方式,默认值是TField的Alignment。
.ButtonStyle设为cbsAuto表示加上一个组合框。设为cbsEllipsis表示加上一个省略号按钮,当用户单击此按钮将触发OnEditButtonClick。设为cbsNone表示既没有组合框也没有省略号按钮。
.Color用于设置背景颜色,默认值是TDBGrid的Color。
.DropDownRows 下拉列表框中的项数,默认值是7。
.Expanded如果列对象所对应的字段是ADT或数组字段,这个属性用于控制是否展开。l FieldName用于指定一个字段,可以让它空着。
.ReadOnly设为True表示该列是只读的,不能编辑。
.Width用于设置列的宽度,默认值是TField的DisplayWidth。
.Font用于设置该列中字符的字体,默认值是TDBGrid的Font。
.PickList用于建立一个静态的列表。
.Title这是个TColumnTitle对象,用于设置列的标题。
其中,TColumnTitle具有下列属性:
.Alignment用于设置标题的对齐方式,设为taLeftJustify表示左对齐,设为taCenter表示居中,设为taRightJustify表示右对齐。
.Caption用于指定标题的文字,默认值是TField的DisplayLabel。
.Color用于设置标题的背景颜色,默认值是TDBGrid的FixedColor。
.Font用于设置标题的字体,默认值是TDBGrid的TitleFont。
12.7.5 显示ADT和数组字段的值
Delphi 4的TDBGrid构件完全支持Oracle 8的对象字段。
如果数据集构件的ObjectView属性设为True,对象字段就可以展开或折叠。当对象字段展开的时候,原来的一列就分为若干列,每一列显示一个子字段的值,每一个子字段所在的列都有自己的标题栏,这样可以明显地看出字段之间的包含关系。当对象字段折叠的时候,就只显示一个字符串,该字符串列出了每个子字段的值,彼此之间用逗号隔开。
如果数据集构件的ObjectView属性设为False,每个子字段与对象字段一样,都单独占一列。
下面介绍几个与对象字段有关的属性。一是TColumn的Expandable属性,如果设为True,表示该列是可展开的。二是TColumn的Expanded属性,用于测试列当前是否已展开。三是TDBGrid的MaxTitleRows属性,用于设置栅格中最多可出现几行标题。四是TDataSet的ObjectView,这个属性前面已介绍过了。五是TColumn的ParentColumn属性,用于返回显示父字段的列对象。
图12.7演示了ObjectView属性设为False的情况:
图12.7 ObjectView属性设为False的情况
图12.8演示了ObjectView属性设为True但对象字段被折叠的情况:
图12.8 ObjectView属性设为True但对象字段被折叠的情况
图12.9演示了ObjectView属性设为True但对象字段被展开的情况:
图12.9 ObjectView属性设为True但对象字段被展开的情况
12.7.6 设置栅格的选项
可以在设计期设置栅格的选项,这就要用到TDBGrid构件的Options属性。Options属性是一个集合,可以包含下列元素:
.dgEditing如果包含这个元素(下同),允许用户修改栅格中的数据;
.dgAlwaysShowEditor栅格自动处于编辑状态,否则需按F2才进入编辑状态;
.dgTitles显示列的标题;
.dgIndicator当前行的最左端将显示一个4符号;
.dgColumnResize列的宽度可以重设;
.dgColLines列与列之间用线分开;
.dgRowLines行与行之间用线分开;
.dgTabs可以用Tab键和Shift+Tab键在列与列之间移动输入焦点;
.dgRowSelect用户可以选择一整行,否则就只能选择一个单元;
.dgAlwaysShowSelection即使输入焦点移走,选择的单元仍然保持选择状态;
.dgConfirmDelete按Ctrl+Delete删除一行时将显示一个确认框,让用户确认;
.dgCancelOnExit在栅格退出的时候,任何未决的修改将被取消
.dgMultiSelect用户可以在栅格中选择多行,相当于一个复选的列表框。
12.7.7 在运行期响应用户的动作
TDBGrid构件具有几个事件,用于响应用户的动作。由于栅格是由行和列构成的,因此,在事件句柄中,首先要区分当前是在哪一列上。下面列出了这些事件:
.OnCellClick当用户单击一个单元时将触发这个事件。
.OnColEnter当输入焦点进入某一列时将触发这个事件。
.OnColExit当输入焦点离开某一列时将触发这个事件。
.OnColumnMoved当用户把某一列移到另一个位置时将触发这个事件。
.OnDblClick当用户在栅格中双击时将触发这个事件。
.OnDragDrop当用户在栅格中释放被拖曳的对象时将触发这个事件。
.OnDragOver当用户拖着一个对象经过栅格时将触发这个事件。
.OnDrawColumnCell当某个单元需要重画时将触发这个事件。
.OnDrawDataCell 在State属性设为csDefault的情况下,当某个单元需要重画时将触发这个事件。
.OnEditButtonClick当用户单击省略号按钮时将触发这个事件。
.OnEndDrag当用户释放了鼠标时将触发这个事件。
.OnEnter当栅格得到输入焦点时将触发这个事件。
.OnExit当栅格失去输入焦点时将触发这个事件。
.OnKeyDown当用户按下一个键时将触发这个事件。
.OnKeyPress当用户按下一个可见的字符键时将触发这个事件。
.OnKeyUp当用户释放一个键时将触发这个事件。
.OnStartDrag当用户开始进行拖放操作时将触发这个事件。
.OnTitleClick当用户单击某一列的标题时将触发这个事件。
12.7.8 一个特殊的数据库栅格
TDBGrid构件的特点是一行只显示一个记录,但可以设置每一列的属性。 TDBCtrlGrid构件的特点是每一行都是一个窗格(TPanel对象),可以在窗格上放一些控件。例如,可以把一个TDBEdit构件放到窗格上,让它显示某个字段的值,还可以在编辑框的旁边加一个TLabel构件作为标签。TDBCtrlGrid的另一个特点是一行上可以有多个窗格,也就是说,一行可以显示多个记录,窗格的高度和宽度都是可以调整的。
首先,要把一个TDBCtrlGrid构件放到窗体上,设置它的DataSource属性指定一个TDataSource构件。刚开始的时候,TDBCtrlGrid构件有三行和一列。
接着,要把一些数据控件放到TDBCtrlGrid构件的“设计单元”上,设置这些数据控件的DataField属性指定要显示的字段。
“设计单元”通常是最上边或最左边的那个单元,与其他单元在外观上的区别是它没有斜线底纹。
注意:只能把数据控件放到TDBCtrlGrid构件的“设计单元”上,当程序编译运行后,会自动把“设计单元”上的数据控件复制到其他单元,每个单元显示不同的记录。
图12.10演示了TDBCtrlGrid构件的用法:
图12.10 TDBCtrlGrid构件的用法
下面列出了TDBCtrlGrid构件的属性:
.AllowDelete设为True表示允许删除记录。
.AllowInsert设为True表示允许插入记录。
.ColCount用于设置每一行的窗格数,默认是1。
.Orientation设为goVertical表示从上到下显示记录,设为goHorizontal表示从左到右显示记录。
.PanelHeight用于设置窗格的高度,默认是72。
.PanelWidth用于设置窗格的宽度,默认是200。
.RowCount用于设置行数,默认是3。
.ShowFocus如果设为True,显示当前记录的窗格上有一个虚线边框。
.SelectedColor用于设置显示当前记录的窗格的背景颜色。
12.8 导 航 器
导航器是用TDBNavigator构件实现的,它可以让用户方便地浏览和操纵记录。导航器最多可由10个按钮组成,包括:
.First第一个记录;
.Prior前一个记录;
.Next后一个记录;
.Last最后一个记录;
.Insert插入一个空白的记录;
.Delete删除当前记录;
.Edit允许编辑当前记录;
.Post使未决的编辑操作有效;
.Cancel取消未决的编辑操作;
.Refresh用数据集中的最新的数据刷新记录。
可以根据需要,在导航器上有选择地显示部分按钮,这就要用到VisibleButtons属性。
在设计期,可以在对象观察器中选择要显示在导航器上的按钮。在运行期,您可以通过编程选择要显示在导航器上的按钮。例如,假设要用同一个导航器为两个数据集导航,其中一个允许用户编辑数据,另一个不允许用户编辑数据。当在两个数据集之间切换时,应当动态地改变导航器上的按钮,因为对于不允许用户编辑数据的数据集来说,导航器上不应当出现Insert、Delete、Edit、Post、Cancel和Refresh等按钮。下面代码把这些按钮隐藏:
Procedure TForm1.CustomerCompanyEnter(Sender :TObject);
Begin
If Sender = CustomerCompany then
Begin
DBNavigatorAll.DataSource := CustomerCompany.DataSource;
DBNavigatorAll.VisibleButtons := [nbFirst,nbPrior,nbNext,nbLast];
End
Else
Begin
DBNavigatorAll.DataSource := OrderNum.DataSource;
DBNavigatorAll.VisibleButtons := DBNavigatorAll.VisibleButtons + [nbInsert,nbDelete,nbEdit,nbPost,nbCancel,nbRefresh];
End;
End;
这里顺便说一下,当同一个导航器需要为几个数据集导航时,就要动态地设置DataSource属性。假设窗体上有两个TDataSource构件,分别叫CustomersSource和OrdersSource,它们的DataSet属性分别指定CustomersTable表和OrdersTable表,另外,窗体上有两个TDBEdit构件,分别叫CustomersEdit和OrdersEdit,它们的DataSource属性分别指向CustomersSource和OrdersSource。下面这段程序为这两个TDBEdit构件写了一个公共的事件句柄来处理OnEnter事件,当用户在CustomersEdit 上编辑时,导航器将为CustomersTable导航,当用户在OrdersEdit上编辑时,导航器将为OrdersTable导航。
Procedure TForm1. CustomerEditEnter(Sender: TObject);
Begin
If Sender = CustomerEdit Then
DBNavigatorl.DataSource := CustomerEdit.DataSource;
Else
DBNavigator1.DataSource := OrderEdit.DataSource;
End;
12.9 数 据 源
TDataSource构件是一个非可视的构件,它充当了数据集和数据控件之间的桥梁。每一个数据控件都必须指定一个数据源(TDataSource构件),相应地,TDataSource构件的DataSet属性必须指定一个数据集。下面简单介绍一下TDataSource构件的属性和事件。
DataSet属性用于指定一个数据集。在设计期,可以在对象观察器中为DataSet属性选择一个数据集。在运行期,可以通过代码动态地选择数据集。程序示例如下:
With CustSource Do
Begin
If DataSet = 'Customers' then
DataSet := 'Orders'
Else
DataSet := 'Customers';
End;
也可以指定另一个窗体上的数据集构件,例如:
Procedure TForm2. FormCreate (Sender : TObject);
Begin
DataSource1.Dataset := Form1.Table1;
End;
一般情况下,TDataSource构件的名称是无关紧要的。不过,TDataSource构件的名称应当能反映它所连接的数据集,例如,假设TDataSource构件连接的数据集叫Customers,相应地,TDataSource构件的名称最好叫CustomersSource。
Enabled属性用于控制TDataSource构件是否与数据集连接,设为True表示连接,设为False表示暂时断开连接。如果Enabled属性设为False,凡是连接于这个数据源的数据控件将变成空白。
如果AutoEdit属性设为True,当用户在数据控件中键入字符时,数据集就自动进入dsEdit状态。如果AutoEdit属性设为False,程序必须调用Edit函数才能进入dsEdit状态。
当数据集的当前记录的位置发生变化时将触发OnDataChange事件,这可能是因为程序调用了Next、Previous、Insert等方法。
当前记录的数据将要被更新时将触发OnUpdateData事件,这可能是因为调用了Post。在处理这个事件的句柄中,可以对数据进行校验。
当State属性发生变化时将触发OnStateChange事件。例如,可以用一个标签动态地显示当前的状态。程序示例如下:
ProcedureTForm1.DataSource1.StateChange(Sender:TObject);
var
S:String;
Begin
Case CustTable.State of
dsInactive: S := 'Inactive';
dsBrowse: S := 'Browse';
dsEdit: S := 'Edit';
dsInsert: S := 'Insert';
dsSetKey: S := 'SetKey';
End;
CustTableStateLabel.Caption := S;
End;
利用OnStateChange事件也可以动态地允许或禁止按钮和菜单项,程序示例如下:
Procedure Form1.DataSource1.StateChange(Sender: TObject);
Begin
CustTableEditBtn.Enabled := (CustTable.State = dsBrowse);
CustTableCancelBtn.Enabled := CustTable.State in [dsInsert, dsEdit, dsSetKey];
...
End;