这里为读者提供一些实用的开发技巧和某些常见问题的解决途径。很多程序设计人员都有这样的体会,那就是往往自己冥思苦想某个问题的时候,旁人的一句点拨就可以拨云见日,灵感突现。因此下面以FAQ的形式列举了在开发过程中可能会遇到的问题,以飨读者。
1.1 关于Delphi的BDE
Delphi操作数据库主要是利用BDE来进行。BDE是基于32位Windows内核的数据库引擎和连接工具,支持现有的大多数数据库。它具有如下一些特点:
1. 为多种数据库格式提供统一的应用程序接口,包括任何ODBC数据源。
2. 适应C/S数据库应用的开发,程序设计人员可以访问所有本地和服务端的数据,并且很容易实现应用的向上兼容性。
3. 对于Delphi标准的数据库访问(如Paradox和dBASE),BDE提供的连接方式是最快的。
4. 直接实时的访问数据源。
1.1.1 如何自定义BDE的驱动程序
BDE中按照各个文件的作用可分为以下几部分,这些文件均保存在“…\Program Files\Common Files\Borland Shared\BDE”目录下。
1. 必备文件
Blw32.dll:语言驱动函数库。
Idapi32.dll:BDE基本函数库。
Fareast.bll、Usa.bll:远东语言及美国语言驱动程序,对中文软件不可缺少。
Idr20009.dll:错误信息库。
2. 数据库驱动程序(可选)
Idasci32.dll:Ascii文本数据库驱动程序函数库;
Iddao32.dll:Access数据库驱动程序函数库;
Iddbas32.dll:dBase数据库驱动程序函数库;
Idodbc32.dll:ODBC数据库驱动程序函数库;
Idpdx32.dll:Paradox数据库驱动程序函数库。
3. 其他驱动程序和配置文件(可选)
Idbat32.dll:批操作驱动程序函数库,如果不用TBatchMove组件或DbiBatchMove类函数,可以不要。
Iddr32.dll:Data Repository驱动程序函数库,如果不用Data Repository功能,可以不要。
Idprov32.dll:BDE DataSet provide驱动程序函数库,如果不用TProvider组件,可以不要。
Idqbe32.dll:QBE驱动程序函数库,如果不用Query By Example,可以不要。
Idsql32.dll:SQL查询驱动程序函数库,如果不用TQuery进行查询,可以不要。
Idapi32.cfg:BDE配置文件,如果程序中没有特殊要求,可以不要。也可以在BDE管理器中设置正确后再分发。
4. 其他文件(一般不用)
BDE32.HLP、BDE32.CNT:BDE帮助文件。
BdeAdmin.exe、BdeAdmin.HLP、BdeAdmin.CNT:BDE管理器及帮助文件。
*.BLL:其他国家和地区的语言驱动程序。
DataBump.EXE、DataBump.HLP:数据库数据转移工具及帮助文件。
Localsql.HLP、Localsql.CNT:SQL查询语句帮助文件。
Sqllnk32.HLP、Sqllnk32.CNT:SQL连接帮助文件。
1.1.2 如何注册BDE
安装BDE仅仅复制前面提到的各种文件是不行的,还要等到向注册表注册之后才可以使用。
必需的注册表项目包括:
1. BDE动态链接库文件位置设置(如图1所示)
Key:HKEY_LOCAL_MACHINE\Software\Borland\Database Engine
Item:DLLPATH
Value:BDE动态链接库文件所在位置
图1 BDE动态链接库文件位置设置
2. BDE语言驱动文件路径设置(如图2所示)
Key:HKEY_LOCAL_MACHINE\Software\Borland\BLW32
Item:BLAPIPATH
Value:BDE语言驱动文件所在路径
图2 BDE语言驱动文件路径设置
3. 定义可用的BDE语言驱动文件(如图3所示)
Key:HKEY_LOCAL_MACHINE\Software\Borland\BLW32
Item:LOCALE_LIB#" (#表示数字, 如"LOCALE_LIB1"、"LOCALE_LIB3"等)
Value:指定各BDE语言驱动文件
图3定义可用的BDE语言驱动文件
1.1.3 如何在运行期检测和建立数据库别名
在前面学习的案例中,数据库别名的建立、修改和删除等维护工作,一般在BDE Administrator中进行,并在设计期就已经设置完毕,但这些功能在运行期将如何实现呢?在Delphi的BDE中,当数据库类型为STANDARD时,其别名定义最为简单,但这时仅能使用Paradox,dBASE,ASCIIDRV三种数据库作为默认的驱动程序。另外还需定义数据库存储路径(PATH)和ENABLE BCD,这样才能建立一个完整的数据库别名。
Delphi的数据库应用程序能自动提供一个Session组件,这个Session组件即为应用程序与BDE的接口。
1. 检测别名
通过调用Session.GetAliasNames(list:Tstrings)方法,可将当前BDE配置中的所有数据库别名的名称存放到List字符串列表中。然后调用list.IndexOf(需要检测的别名\')函数,该函数的返回值可用来检测数据库别名是否存在(若其值为-1则不存在)。
2. 添加别名
使用Session组件的过程AddStandardAlias(constName,Path,DefaultDriver:string),可以添加一个标准类型的数据库别名。如添加一个数据库别名为NewAlias,其默认数据库驱动程序为Paradox、存储路径为c:\instance1,则可以使用下面的语句。
Session.AddStandardAlias(\'NewAlias\',\' c:\instance1\',\'Paradox\');
3. BDE配置文件存盘
使用Session.SaveConfigFile语句可将设置信息保存。
一般可以在窗体的OnCreate事件中添加检测和创建数据库别名的程序代码,下面就是一个在运行期实现上述功能的例子。首先单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加一个Table组件,用以提供TSession对象。然后在窗体的OnCreate事件中添加如下所示的代码,程序执行时,系统会首先检测NewAlias别名是否存在,若不存在,提示用户是否创建这个别名,如图4所示。
Procedure TForm1.FormCreate(Sender: TObject);
var
sl:TStringList; //字符串列表变量
re:Integer;
begin
sl:=TStringlist.Create;
Session.GetAliasNames(sl); //取得别名列表
if (sl.IndexOf(\'NewAlias\')=-1) then //判断别名是否存在
begin
re:=Application.MessageBox(\'别名NewAlias不存在,现在创建吗?\',\'BDE信息窗口\',mb_OKCancel);
if re=IDCANCEL then begin
sl.Free;
Exit;
end;
Session.AddStandardAlias(\'newAlias\',\'c:\instance1\',\'Paradox\');
//增加一个名为NewAlias的数据库别名
Session.SaveConfigFile; //保存BDE配置文件
end ;
sl.free;
end;
图4 提示信息
1.1.4 Delphi连接数据库的方式有哪些
---- Delphi访问数据库的方式主要有3种,包括直接访问,如访问标准数据库类型Paradox和dBASE;通过ODBC访问,如访问Access、Foxpro等数据库;通过内嵌(Native)方式访问数据库,如访问SQLServer、Oracle、DB2等。下面以Table组件为例说明这3种方式的特点和用法。
1. 直接访问
----Delphi可以直接访问诸如Paradox和dBASE的标准数据库类型,而不需要什么特别的设置,只需要把文件路径或数据库别名赋值给Table组件的属性DatabaseName就可以访问该路径下的数据表了。
2. 通过ODBC访问
----访问Paradox和dBASED以外的数据库,通常可通过ODBC来实现。Delphi可以访问支持ODBC的数据库系统,如Access、SQLServer和Oracle等。当然,通过ODBC访问数据库时,首先要使用Windows的控制面板或Delphi的数据库引擎建立ODBC数据源。
3. 通过内嵌方式访问
Delphi中还可以不使用ODBC,而以内嵌方式访问SQLServer、Oracle、DB2等数据库系统。这需要使用数据库别名来指定数据库,数据库别名可以事先建立,也可以在程序运行时动态创建。前者称为静态别名,后者称为动态别名。使用数据库别名来访问数据库的方法和使用ODBC数据源的情形相同,这里不再赘述。
通过内嵌方式访问数据库的静态别名必须在BDE中建立。以访问SQLServer数据库为例,在建立别名时必须指定数据库服务器的名称(Servername)、主机名(Hostname)以及要访问的数据库名称(Databasename),可以指定登录用户名(Username)和密码(Password)等。
图5 设置Database组件
通过动态创建的别名来访问数据库必须使用Database组件。用户可以用鼠标双击Database组件,出现参数设置窗口,如图5所示。在“Drivername”一栏选择要访问的数据库系统,如“MSSQL”,然后选择按钮“Defaults”,就会把BDE中该数据库系统所需的参数名称和缺省值加入到“Parameteroverrides”列表中。根据实际情况更改参数中的“Servername”、“Databasename”等项。最后单击OK按钮完成设置。
比较可知,通过内嵌方式访问数据库要比通过ODBC访问数据库速度快一些。而且,内嵌方式可以在程序中动态设置连接数据库所需的参数,用户可不必设置ODBC数据源,从而降低了对用户技术水平的要求,并且减少了用户的工作量。因此从系统配置的难易和复杂程度来看,使用内嵌方式开发出的数据库应用系统更便于普通用户使用。
1.1.5 如何获取BDE的版本信息
单击Delphi工具栏中的“New Form”按钮,新建一窗体。向该窗体中添加1个Memo组件,并设置其Align属性为alclient。在窗体对应的单元文件的USES部分添加dbierrs, DBTables两个引用库文件。在下面的单元文件中,fDbiGetSysVersion函数将返回一个称为SysVersion的结构,并将最后结果显示在窗体的Memo中。
/*该窗体的单元文件代码*/
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,dbierrs, DBTables, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function fDbiGetSysVersion(SysVerList: TStringList): SYSVersion;
var
Month, Day, iHour, iMin, iSec: Word;
Year: SmallInt;
begin
Check(DbiGetSysVersion(Result));
if (SysVerList <> nil) then
begin
with SysVerList do
begin
Clear;
Add(Format(\'数据引擎版本号=%d\', [Result.iVersion]));
Add(Format(\'接口级=%d\', [Result.iIntfLevel]));
Check(DbiDateDecode(Result.dateVer, Month, Day, Year));
Add(Format(\'版本日期=%s\', [DateToStr(EncodeDate(Year, Month, Day))]));
Check(DbiTimeDecode(Result.timeVer, iHour, iMin, iSec));
Add(Format(\'版本时间=%s\', [TimeToStr(EncodeTime(iHour, iMin, iSec div 1000, iSec div 100))]));
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var hStrList: TStringList;
Ver: SYSVersion;
begin
hStrList:= TStringList.Create;
try Ver := fDbiGetSysVersion(hStrList);
except
ShowMessage(\'BDE not installed !\');
end;
Memo1.Lines.Assign(hStrList);
hStrList.Destroy;
end;
end.
按F9键运行程序,将得到如图6所示的BDE版本信息。
图6 运行结果窗口
1.1.6 如何维护DBF数据库
事实上,由于Delphi具有快捷易用、功能强大等优点,使得很多FoxPro和VFP的程序设计人员加入到了Delphi阵营。但在FoxPro和VFP的开发环境下,有许多DBF类型的数据表文件需要移植到Delphi中来,因此在Delphi中如何维护和操作这些数据将是一个很重要的问题。下面阐述在Delphi中完成DBF数据库的如下操作:真正删除记录、显示被删除记录、获取当前记录号和恢复被删除记录.
1. 真正删除记录
在Delphi程序中,使用Table或Query组件的Delete方法执行删除记录操作时,系统执行的是软删除,即相当于Foxpro中的“Set Delete Off”的效果。该方法只是将记录用星号标记为删除,实际并没有进行物理上的删除。要想真正删除记录,需要调用BDE函数DbiPackTable,该函数语法为:
functionDbiPackTable(hDb:hDBIDb;hCursor:hDBICur;pszTableName:PChar;pszDriverType:PChar;bRegenIdxs:Bool):DBIResult;
其中hDb为数据库Databse的句柄,hCursor为数据表Table的句柄,pszTableName为要删除记录的数据表名,pszDriverType为要删除记录的数据表的类型,bRegenIdxs表示是否在删除记录后自动更新索引文件。在四个参数中,hDb不能为NULL。hCursor、pszTableName、pszDriverType可以为NULL,但必须提供足够的信息来标识数据表的文件名和类型,当hCursor不为空时,pszTableName和pszDriverType可以为NULL;当pszTableName为数据表的路径和文件名时,hCursor和pszDriverType可以为NULL。
值得注意的是,在删除记录时,如果用Table来实现,则Table必须以Exclusive=True的方式打开。
2. 显示或不显示软删除记录
当DBF数据库中的记录执行软删除后,默认情况下在DBGrid等数据库显示组件中是看不见这些记录的。可以用BDE函数来控制是否显示DBF数据库中被软删除的记录,如同在Foxpro中利用语句Set Delete ON/OFF开关一样。执行这一功能的函数为DbiSetProp,其语法为:
functionDbiSetProp(hObj:hDBIObj;iProp:Longint;iPropValue:Longint):DBIResult;
该函数用来设置DBI对象中某个属性的值。其中Obj为DBI对象名称,这里为数据表Table的句柄,iProp为属性名,可使用软删除属性curSOFTDELETEON,iPropValue为属性值。
3. 获取当前记录号
在Delphi中可以使用BDE函数获取当前记录在数据集中的记录号.该函数为DbiGetRecord,其语法为:
functionDbiGetRecord(hCursor:hDBICur;eLock:DBILockType;pRecBuff:Pointer;precProps:pRECProps):DBIResult;
该函数用来取得当前记录的一些属性。其中,hCursor为数据集的Handle,eLock为对记录加锁的类型,pRecBuff存放记录的缓冲区,precProps为记录属性集,这里面就包含当前记录的记录号。
4. 恢复软删除记录
在Delphi应用程序中,对DBF数据表执行的删除操作为软删除操作。由于物理记录并没有从数据表中删除,因此可以恢复被软删除的记录,只要去掉删除标志即可。完成这一功能需使用函数DbiUndeleteRecord,其语法为:
functionDbiUndeleteRecord(hCursor:hDBICur):DBIResult;
其中,hCursor可以是数据集的Handle。
1.1.7 如何备份数据表
对于数据库应用来说,数据表的备份应该算是一个常用的功能。下面举一例,介绍其实现过程。单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加1个Table组件、1个DataSource组件、1个DBGrid组件和2个Button组件,设置数据库之间的连接关系,并置Table的Active属性为True。窗体的布局设计如图7所示。
图7 窗体布局示例
然后在“备份表”按钮的OnClick事件中添加代码:
Procedure TForm1.Button1Click(Sender: TObject);
begin
QuickCopyTable(table1,\'c:\a.dbf\',true);
ShowMessage(\'数据表备份已完成\')
end;
其中所引用的QuickCopyTable方法的代码如下所示。
Procedure QuickCopyTable(T: TTable; DestTblName: string; Overwrite: Boolean);
var
DBType: DBINAME;
WasOpen: Boolean;
NumCopied: Word;
begin
WasOpen := T.Active; //保存表的Active属性值
if not WasOpen then T.Open; //检查表是否打开
Check(DbiGetProp(hDBIObj(T.Handle),drvDRIVERTYPE,@DBType, SizeOf(DBINAME), NumCopied)); //获得驱动程序类型字符串
Check(DBICopyTable(T.DBHandle, Overwrite, PChar(T.Tablename),
DBType, PChar(DestTblName))); //复制表
T.Active := WasOpen; //恢复表的Active属性值
end;
最后,要在单元文件的Uses备份添加dbierrs库文件。此时单击Delphi工具栏上的“Run”按钮或按下F9键,运行程序,单击“备份表”按钮,如图8所示。所备份的数据表a.dbf内容,如图9所示。
图8 运行结果窗口
图9 备份的a.dbf文件的内容
1.1.8 如何使用过滤器
在操作数据库时,经常需要对数据进行筛选过滤。例如在第四章学生学籍管理系统的案例中,有一个名为jiben.dbf的数据表,它具有XH、XM、CSRQ等多个字段,如果只想查看班级编号为001的学生记录,就需要对数据表中的信息进行过滤。下面是常用的对数据表信息进行过滤的方法:
1. 利用TTable和TQuery的Filter属性
n 在设计时设置Filter属性
例如设置Filter为(bjbh=’001’),然后改变Filtered属性为True。此时将只能看到对应的bjbh(班级编号)字段内容为’001’的记录。另外设置Filter时可以使用的操作符有:<、>、<=、>=、=、<>、AND、OR、NOT。
n 在程序运行期间进行动态过滤
要在程序运行时改变Filter属性,包括两种情况。一是操作符右边为常量,例如语句(table1.Filter:=\'bjbh\'+\'=\'+\'001\';);二是操作符右边不为常量,可能是通过一个变量指定的值,或由一输入框给出的值。此时需要使用Format函数。其代码形式为(Table1.Filter:=Format(\'bjbh\'+\'=\'+\'%S\',[bjbhvalue]);)其中bjbhvalue为已经赋值的一个字符串变量,也可以为其他形式如Edit1.Text。
2. 用ApplyRange筛选数据集的记录
Delphi的DBDEMOS数据库中有一个Customer.db数据表,如果我们想查看顾客号从1000到3000之间的顾客记录。可使用ApplyRange, SetRangeStart, SetRangeEnd过程来完成,其代码为:
Table1.SetRangeStart;
Table1[‘CustNo’]:=1000;
Table1.SetRangeEnd;
Table1[‘CustNo’]:=3000;
Table1.ApplyRange;
需要说明的是该过程只适用于索引的字段。
3. 用OnFilterRecord事件过滤
例如下面的代码:
Procedure TForm1.Table1FilterRecord(DataSet:TDataSet;varAccept:Boolean);
begin
Accept:=DataSet[‘bjbh’]=’001’;
end;
4. 用Query组件的SQL语句
n SQL语句中不包含变量和参数
Select * from Customer Where CustNo>=1000 and CustNo<=3000
n SQL语句中包含参数
Select * from Customer Where CustNo>=:p1
可在程序中给参数p1赋值,读者可参阅前面讲述的案例程序。
n SQL语句中包含变量
此时向Query组件添加SQL语句的代码如下所示:
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(Format(‘Select * from Customer’+’’+’where CustNo=’+’%S’, [CustNoValue]));
Query1.Open;
1.2 关于字符串操作
在程序开发过程中,经常要涉及对字符串的操作。Delphi提供了一个TStrings类,用来完成存储和操作字符串,它的主要行为有:
1. 在列表中添加或删除字符串。
2. 重新排列字符串。
3. 在指定的位置存取字符串。
4. 从文件或流中读取及写入字符串
5. 为列表中的每一个字符串联系一个对象。
TStrings类的继承关系为:TObject-TPersistent
1.2.1 常用的字符串处理函数有哪些
表1列出了常用的字符串处理函数。
表1 常用字符串处理函数
函数名
语法
功能
AnsiCompareStr
function AnsiCompareStr(const S1, S2: string): Integer;
用于比较两个大小写敏感的字符串
AnsiCompareText
function AnsiCompareText(const S1, S2: string): Integer;
用于比较两个大小写不敏感的字符串
AnsiUpperCase
function AnsiUpperCase(const S: string): string;
将字符串转换为全部大写
AnsiLowerCase
function AnsiLowerCase(const S: string): string;
将字符串转换为全部小写
Appendstr
procedure AppendStr(var Dest: string; const S: string); deprecated;
将给定字符串常量添加到目标字符串末尾
CompareStr
function CompareStr(const S1, S2: string): Integer;
用于比较两个大小写敏感的字符串,其结果与区域设置无关
CompareText
function CompareText(const S1, S2: string): Integer;
用于比较两个大小写不敏感的字符串,其结果与区域设置无关
Concat
function Concat(s1 [, s2,..., sn]: string): string;
将一组字符串连接起来
Copy
function Copy(S; Index, Count: Integer): string;
返回字符串的子串
Delete
procedure Delete(var S: string; Index, Count:Integer);
从字符串中删除一个子串
Insert
procedure Insert(Source: string; var S: string; Index: Integer);
在字符串的指定位置插入一个子串
Length
function Length(S): Integer;
返回字符串中中字符的个数
Pos
function Pos(Substr: string; S: string): Integer;
在字符串中搜索子串,返回的是索引值
LeftStr
function LeftStr(const AText: AnsiString; const ACount: Integer): AnsiString;
返回从字符串左边开始指定长度的子串
RightStr
function RightStr(const AText: AnsiString; const ACount: Integer): AnsiString;
返回从字符串末尾向前指定长度的子串
InttoStr
function IntToStr(Value: Integer): string;
将整数转换为字符串
StrtoInt
function StrToInt(const S: string): Integer;
将字符串转换为整数
LowerCase
function LowerCase(const S: string): string;
转换为小写
UpperCase
function UpperCase(const S: string): string;
转化为大写
Val
procedure Val(S; var V; var Code: Integer);
将字符串的值转换为其数字表示式
1.2.2 TStrings对象的属性和方法有哪些
TStrings的一些重要属性和方法如下所示。
Count:该属性定义列表中字符串的数量。
Strings:该属性表示由参数Index指定位置的字符串。0表示第一个字符串,1表示第二个字符串,依此类推。
Text:TStings对象的文本,它包含一些由回车和换行符分开的字符串。
Add方法:该方法在字符串列表的末尾添加一字符串。在调用Add方法加入字符串之后,再返回新字符串的索引值。
AddObject方法:该方法向字符串列表中加入一个字符串及与它相联系的对象。调用AddObject方法之后将返回新字符串和对象的索引值。
Append方法:该方法将在字符串列表中添加一字符串,它与Add方法一样,但不返回值。
Clear方法:该方法将清空字符串列表。
Delete方法:删除指定的字符串。
Destroy方法:析构函数,毁坏一个TStrings对象实例。
IndexOf方法:function IndexOf(const S:string):integer; 该方法的功能是返回字符串S在字符串列表中的索引值。调用IndexOf函数返回的是第一次发现字符串S的位置。其返回值也以0作为起点,若返回0则表示是第一个字符串,返回1表示是第二个字符串,依此类推。如果指定的字符串S不在列表中,则返回-1。需要指出的是,若S在字符串列表中出现的次数大于1,则IndexOf方法返回的是第一次发现的位置值。下面介绍一个IndexOf方法的应用实例。
单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加1个FileListBox组件、1个DirectoryListBox组件和2个Label组件,完成各个组件的属性设置及布局之后,在DirectoryListBox组件的OnChange事件中添加如下所示的代码。然后按F9键执行程序,其结果如图10所示。
图10 执行结果窗口
Procedure TForm1.DirectoryListBox1Change(Sender: TObject);
begin
filelistbox1.Directory:=directorylistbox1.Directory;
if filelistbox1.Items.IndexOf(\'regedit.exe\')>-1 then
showmessage(\'当前是Windows目录\');
end;
Insert方法:该方法在指定位置插入一字符串。其参数Index为给定的位置索引值,参数S为要插入的字符串。
LoadFromFile方法:调用该方法将使用指定的文件填充文本。其参数FileName用来定义文件名,文件中行与行之间以回车和换行符隔开。事实上,LoadFromFile方法是运用Add方法将文件中的每一行添加到字符串列表中的。
SaveToFile方法:与LoadFromFile方法相对应,该方法将列表中的字符串存储于文件中,其参数FileName用来定义文件名。
1.2.3 如何判断输入的字符串是电子邮件地址
选择“File|New Application”菜单命令,在默认建立从窗体中添加1个Label组件、1个Edit组件和1个Bitbtn组件,完成各个组件的属性设置即布局之后,在Bitbtn组件的OnClick事件中加入如下所示的代码。
Procedure TForm1.BitBtn1Click(Sender: TObject);
begin
if isemail(Edit1.Text) then
ShowMessage(\'正确的邮件地址!\')
else
showmessage(\'错误的邮件地址!\');
end;
其中isemail函数的代码如下所示,可将其添加到单元文件的implementation部分。
Function IsEMail(EMail: String): Boolean;
var
s: String;
ETpos: Integer;
begin
ETpos:= pos(\'@\', EMail); //获取@符号的位置
if ETpos > 1 then
begin
s:= copy(EMail,ETpos+1,Length(EMail)); //截取子串
if (pos(\'.\', s) > 1) and (pos(\'.\', s) < length(s)) then
Result:= true
else
Result:= false;
end
else
Result:= false;
end;
按下F9键执行程序,其结果如图11和9-12所示。
图11 用户输入错误的邮件地址窗口
图11 用户输入正确的邮件地址窗口
1.2.4 如何获得字符串中汉字和英文的个数
Microsoft Word具有一项“字数统计”功能,可以统计出文本中汉字和英文的个数,如图12所示。
图12 Microsoft Word的字数统计功能
其设计思路就是通过把字符转换为ASCII码数值来进行判断,Ord函数就可以把字符转换为对应的ASCII码数值,其中值为33-126为键盘可使用字符,值127以上的为未知字符,即汉字。下面完成一个示范程序,选择“File|New Application”菜单命令,在默认的窗体中添加1个Memo组件、2个Label组件和2个Button组件,完成对各个组件的属性设置及布局之后,在“字数统计”按钮的OnClick事件中添加如下所示的代码。
Procedure TForm1.Button1Click(Sender: TObject);
var s:string;
I,e,c:integer;
begin
s:=memo1.text; //Memo的内容
e:=0;c:=0;
for I:=1 to length(s) do
begin
if (ord(s[I])>=33)and(ord(s[I])<=126) then
begin
inc(e);
label1.caption:=\'英文字数:\'+inttostr(e);
end
else
if (ord(s[I])>=127) then
begin
inc(c);
label2.caption:=\'中文字数:\'+inttostr(c div 2);
end;
end;
end;
按下F9键运行程序,其执行结果如图13所示。
图13 执行结果窗口
1.2.5 使用拼音首字母序列实现检索功能
在本书的考勤管理系统中,为了查询中文人名,我们在相应的数据表中添加了姓名编码字段,即提取员工姓名的首字母,作为其姓名编码。这样作的目的实质上就是为了方便查询功能的实现。本节提供的该问题解决途径在某种程度上可以进一步完善考勤信息管理系统案例。
事实上,解决这个问题的思路很简单。那就是要找出汉字表中拼音首字母分别为“A”到“Z”的汉字内码范围,这样对于要检索的汉字,只需要检查它的内码位于哪一个首字母的范围内,就可以判断出它的拼音首字母。
下面完成一个示范程序,选择“File|New Application”菜单命令,在默认的窗体中添加1个Label组件、1个Bevel组件、1个Edit组件和2个ListBox组件。完成对各个组件的属性设置及布局之后,在Edit组件的OnChange事件中添加如下所示的代码。
Procedure TForm1.Edit1Change(Sender: TObject);
var ResultStr:string;
begin
ResultStr:=\'\';
listbox2.Items.Text := SearchByPYIndexStr(listbox1.Items, edit1.Text);
end;
这里所使用的SearchByPYIndexStr函数的代码如下所示,其功能是在字符串列表中检索符合拼音索引字符串的所有字符串。可将其添加到窗体单元文件的implementation部分。
Function SearchByPYIndexStr( SourceStrs:TStrings; PYIndexStr:string):string;
label NotFound;
var
i, j :integer;
hzchar :string;
begin
for i:=0 to SourceStrs.Count-1 do
begin
for j:=1 to Length(PYIndexStr) do
begin
hzchar:=SourceStrs[i][2*j-1]+ SourceStrs[i][2*j];
if (PYIndexStr[j]<>\'?\') and (UpperCase(PYIndexStr[j]) <>
GetPYIndexChar(hzchar)) then
goto NotFound;
end;
if result=\'\' then
result := SourceStrs[i]
else
result := result + Char(13) + SourceStrs[i];
NotFound:
end;
end;
上面程序中引用的GetPYIndexChar函数用于获取指定汉字的拼音索引字母,如“杜”的索引字母是“D”。该函数的代码如下所示,也将其添加到窗体单元文件的implementation部分。
Function GetPYIndexChar( hzchar:string):char;
begin
case WORD(hzchar[1]) shl 8 + WORD(hzchar[2]) of
$B0A1..$B0C4 : result := \'A\';
$B0C5..$B2C0 : result := \'B\';
$B2C1..$B4ED : result := \'C\';
$B4EE..$B6E9 : result := \'D\';
$B6EA..$B7A1 : result := \'E\';
$B7A2..$B8C0 : result := \'F\';
$B8C1..$B9FD : result := \'G\';
$B9FE..$BBF6 : result := \'H\';
$BBF7..$BFA5 : result := \'J\';
$BFA6..$C0AB : result := \'K\';
$C0AC..$C2E7 : result := \'L\';
$C2E8..$C4C2 : result := \'M\';
$C4C3..$C5B5 : result := \'N\';
$C5B6..$C5BD : result := \'O\';
$C5BE..$C6D9 : result := \'P\';
$C6DA..$C8BA : result := \'Q\';
$C8BB..$C8F5 : result := \'R\';
$C8F6..$CBF9 : result := \'S\';
$CBFA..$CDD9 : result := \'T\';
$CDDA..$CEF3 : result := \'W\';
$CEF4..$D188 : result := \'X\';
$D1B9..$D4D0 : result := \'Y\';
$D4D1..$D7F9 : result := \'Z\';
else
result := char(0);
end;
end;
完成之后,按下F9键运行程序,用户在编辑框中输入人名的拼音首字母后,在ListBox2中会自动检索对应的姓名,如图14所示。
图14 执行结果窗口
1.3 关于Delphi的VCL
OOP系统开发的核心是组件的使用,Delphi所提供的VCL类似于Visual C++的MFC和Borland C++的OWL类库,但C++的类库在可视化程序设计、易用性等方面远不如VCL库。用户使用VCL库可以快速制作系统交互界面,实现本地和远程数据库的存取,设计应用程序模板和ActiveX控件等。本节主要介绍一些有关VCL的技巧及应用。
1.3.1 深入了解RichEdit组件
RichEdit组件提供了一个标准的、丰富的文本操作界面,该组件允许用户输入不同字体属性和不同段落属性的文本。RichEdit组件与Memo组件非常相似,都可以编辑多行文本,但Memo编辑器中的文本只能有一种格式,而RichEdit组件中的文本却可以包含多种字体和颜色。
1.3.1.1 如何获得RichEdit组件的当前行号
使用RichEdit组件的lines.count属性可以得到该组件中文本的总行数,但不能知道光标当前所在行号,而要实现这个功能,则需调用em_ LineFromChar消息。
下面完成一个示范程序,选择“File|New Application”菜单命令,在默认的窗体中添加1个RichEdit组件和1个Button组件。完成对各个组件的属性设置及布局之后,在Button组件的OnClick事件中添加如下所示的代码。
Procedure TForm1.Button1Click(Sender: TObject);
var
CurrentLine:Integer;
begin
CurrentLine:=richedit1.Perform(EM_LineFromChar,richedit1.SelStart,0)+1;
Application.MessageBox(PChar(\'当前行号是\'+IntToStr(CurrentLine)),\'消息\',mb_icon information);
end;
需要说明的是,由于EM_LineFromChar消息返回的行号中第一行为0,所以要在代码中加1。按下F9键运行程序,然后单击窗口中的Button按钮,其结果如图15所示。
图15 执行结果窗口
1.3.1.2 如何执行撤销操作
对于Memo组件来说,实现撤销功能是不需添加代码的,只需清除其Popupmenu属性,这样在程序运行时就能用鼠标右键激活一个快捷菜单,如图16所示,选择该菜单中的“撤销”命令即可。
图16 Memo的快捷菜单
而RichEdit组件却不能这样完成撤销操作。下面完成一个示范程序,选择“File|New Application”菜单命令,在默认的窗体中添加1个RichEdit组件和1个PopupMenu组件。双击PopupMenu组件,添加菜单项“撤销”,并在该菜单项的OnClick事件中添加如下所示的代码。
Procedure TForm1.N1Click(Sender: TObject);
begin
RichEdit1.Perform(EM_UNDO,0,0); //执行撤销操作
end;
需要说明的是,在执行撤销操作之前还要检查是否允许撤销,从而开启或关闭快捷菜单中的“撤销”命令,因此需在RichEdit1的OnChange事件中添加如下所示的代码。
Procedure TForm1.RichEdit1Change(Sender: TObject);
begin
N1.Enabled:=RichEdit1.Perform(EM_CANUNDO,0,0)<>0;
end;
按下F9键运行程序,然后右键单击窗口中的RichEdit区域,弹出如图17所示的快捷菜单,选中部分文本,按下Delete键,此时再右键单击窗口中的RichEdit区域,将弹出如图18所示的快捷菜单,此时“撤销”命令已激活,可以完成相应功能。
图17 “撤销”命令失效 图18 “撤销”命令激活
1.3.2 如何调整Memo中Tab的距离
使用制表键Tab在文本编辑器移动是用户频繁使用的操作,在Microsoft Word中可以自定义Tab移动的距离,那么在Delphi中如何实现对文本编辑类组件如Memo中Tab距离的控制呢?下面完成一个示范程序,选择“File|New Application”菜单命令,在默认的窗体中添加1个Memo组件,然后在窗体的OnCreate事件中添加如下所示的代码。
Procedure TForm1.FormCreate(Sender: TObject);
var
DialogUnitsX : LongInt;
PixelsX : LongInt;
i : integer;
TabArray : array[0..4] of integer;
begin
Memo1.WantTabs := true;
DialogUnitsX := LoWord(GetDialogBaseUnits); //返回对话框的基本单位
PixelsX := 30; //可设置该值决定Tab移动距离
for i := 1 to 5 do begin
TabArray[i - 1] :=
((PixelsX * i ) * 4) div DialogUnitsX; //计算TabArry
end;
SendMessage(Memo1.Handle, EM_SETTABSTOPS,5, LongInt(@TabArray));
//发送设置Tab距离消息
Memo1.Refresh; //刷新Memo
end;
按下F9键运行程序,然后在Memo中使用Tab键,如图19所示,图中使用了不同的PixelsX值。
图19 PixelsX=20和50的执行结果窗口
1.3.3 如何使用TApplication类
前面我们已经用到过TApplication类的一些属性和方法,如Application.messagebox等,这里作一个汇总。
1. 检测当前Windows程序是否被激活
TApplication类有一个Active属性,该属性用来描述当前运行的程序是否被激活。可以使用如下所示的代码进行检测:
If Application.Active=False then
ShowMessage(’当前窗口没有被激活’);
2. 取得当前程序名
TApplication类的EXEName属性可以返回该可执行程序的完整文件名(包括路径)。其代码如下所示:
ShowMessage(Application.ExeName);
3. 更改程序最小化时的标题
使用TApplication类的Title属性。可以控制程序最小化时的标题,而窗口中标题栏的标题则是由Form的Caption属性决定的。另外,在本书第1章曾经提到在设计期通过使用“Project|Options”菜单命令来设置应用程序的标题,实质上也是修改TApplication类的Title属性。其代码如下所示:
Application.Title:=’程序的标题’;
Form1.Caption:=’窗口的标题’;
4. 标识程序的主窗口
Windows应用程序一般都有一个主窗口。可使用TApplication类的MainForm属性返回程序的主窗口。
5. 弹出提示框
本书前面章节已经提到了TApplication类的MessageBox 函数,这里不再赘述。
6. 控制窗口的尺寸
可以用Application的事件来调整窗口大小。主要是使用以下两个过程:
Application.Minimized;
Application.Restore;
前一个过程用来将程序的主窗口最小化,而后一个过程用来将最小化的窗口恢复到原来的尺寸。
7. 链接联机帮助文件
TApplication的CurrentHelpFile属性能够指定当前程序所用的帮助文件的文件名。这个属性经常与另一个方法联合在一起使用。通过它们的命令组合,就可以使系统弹出一个显示某主题的联机帮助文件。
例如:Application.HelpFile := \'联机帮助文件名\';
Application.HelpJump(\'联机帮助文件的主题’)
8. 终止程序
尽管可以使用关闭主窗口的方法终止程序,但更好的办法是使用Application的Terminate过程。
1.3.4 通用的VCL属性有哪些
很多组件都有共同的属性,应该说了解VCL的公共属性对于程序设计是非常重要的。表2列出了一些通用属性。
表2 VCL公共属性
属性
范围
含义
Action
某些组件
指定与组件连接的活动对象
Align
某些组件
确定组件的对齐方式
Anchors
大部分组件
说明与组件连接的窗体位置点
Autosize
某些组件
确定组件是否依据内容决定本身大小
BiDiMode
所有组件
支持自右向左输入的语言
BorderWidth
窗口组件
定义边框宽度
BoundsRect
所有组件
定义运行期组件的边框矩形
Caption
大部分组件
定义组件标题
ComponentCount
所有组件
定义当前组件拥有的子组件数目(在运行期调用)
ComponentIndex
所有组件
定义当前组件中子组件的索引位置(在运行期调用)
Components
所有组件
定义当前组件拥有的子组件数组,可用索引值调用
Constraints
所有组件
在更改组件大小时先哲组件的最大和最小尺寸
Color
大部分组件
定义当前组件的表面颜色或背景颜色
Ctrl3D
大部分组件
定义组件是否具有三维效果
Cursor
所有组件
定义当前组件上的鼠标形状
DragCursor
大部分组件
定义当前组件在拖动时的鼠标形状
DragKind
大部分组件
若DragMode为自动,则可选择拖动还是停放
DragMode
大部分组件
定义组件的拖放行为
Enabled
所有组件
定义组件是否激活
Font
所有组件
定义组件内显示的文本字体
Handle
窗口类组件
定义窗口句柄
Height
所有组件
定义组件的高度
Hint
所有组件
定义提示文本
Left
所有组件
定义组件左上角的水平坐标值
Name
所有组件
定义组件名
Owner
所有组件
定义主组件
PopupMenu
所有组件
定义用户右键单击时弹出的快捷菜单
ShowHint
所有组件
定义是否激活提示文本
TabOrder
窗口类组件
定义父组件中的切换顺序
Tag
所有组件
用于存储定制的非定义数据的长整型变量
Top
所有组件
定义组件左上角的垂直坐标
Visible
所有组件
定义组件是否可视
Width
所有组件
定义当前组件的宽度
1.3.5 通用的VCL方法有哪些
VCL的方法由函数和过程组成。表3列出了一些通用的组件对象方法。
表3 VCL 通用方法
属性
含义
BeginDrag
开始手工拖动
BringToFront
将组件置于其他组件前
CanFocus
确定是否允许组件接收焦点
ClientToScreen
将客户区坐标转换为屏幕坐标
ContainsControl
确定某组件是否属于当前组件
Create
建立组件的实例
Destroy
析构函数,毁坏实例
Dragging
说明组件是否被拖动
EndDrag
终止手工拖动
ExecuteAction
激活与组件相连的操作
FindComponent
返回Components数组中具有给定名称的组件
Focused
确定组件是否具有焦点
Free
从内存中清除对象
GetTexBuf
检索组件的文本(或标题)
GetTextLen
返回组件的文本(或标题)
Hide
隐藏组件
InsertComponent
向组件列表中添加新组件
Invalidate
强制重新绘制组件
ManualDock
手工激活停放
ManualFloat
将停放组件设置为浮动组件
RemoveComponent
从Components列表中删除一个组件
ScaleBy
用指定的百分比设置组件的比例
ScreenToClient
将屏幕坐标转换为客户区坐标
ScrollBy
滚动显示组件内容
SendToBack
将当前组件置于其他组件后
SetBounds
改变组件的位置和大小
SetFocus
赋予组件输入焦点
SetTextBuf
设置组件的文本(或标题)
Show
显示组件
Update
重新绘制组件
1.4 综合
本节列举了一些程序设计过程中经常遇到的问题解答,作为前面案例学习的补疑。
1. 在Delphi中如何调用外部的EXE文件
首先在单元文件的Uses语句中添加WinProcs,然后在需要调用外部EXE文件的地方添加代码:
begin
WinExec(......);
end.
WinExec为API函数,其语法为:UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow );
2. 如何启动控制面板中的各项设置功能
在本书第3章的案例中曾经使用WinExec这个API函数启动了控制面板中的“系统信息”功能,而启动控制面板中的其他选项功能,如“添加/删除程序”、“显示”等,也可以使用如下所示的语句来完成(以中文Windows 2000操作系统为例) 。
…
var x:cardinal; //定义变量
begin
//启动“控制面板”
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL\',9);
//启动“辅助功能选项”的“键盘”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1\',9);
//启动“辅助功能选项”的“声音”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2\',9);
//启动“辅助功能选项”的“显示”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3\',9);
//启动“辅助功能选项”的“鼠标”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4\',9);
//启动“辅助功能选项”的“常规”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5\',9);
//启动“添加/删除程序”的“添加新程序”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,1\',9);
//启动“添加/删除程序”的“添加/删除Windows组件”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,2\',9);
//启动“添加/删除程序”的“更改或删除程序”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,3\',9);
//启动“显示”的“背景”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0\',9);
//启动“显示”的“屏幕保护程序”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1\',9);
//启动“显示”的“外观”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2\',9);
//启动“显示”的“设置”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3\',9);
//启动“Internet”的“常规”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,0\',9);
//启动“Internet”的“安全”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,1\',9);
//启动“Internet”的“内容”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,2\',9);
//启动“Internet”的“连接”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,3\',9);
//启动“Internet”的“程序”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,4\',9);
//启动“Internet”的“高级”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,5\',9);
//启动“区域选项”的“常规”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,0\',9);
//启动“区域选项”的“数字”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,1\',9);
//启动“区域选项”的“货币”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,2\',9);
//启动“区域选项”的“时间”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,3\',9);
//启动“区域选项”的“日期”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,4\',9);
//启动“区域选项”的“输入法区域设置”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,5\',9);
//启动“游戏选项”的“控制器”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Joy.cpl,,0\',9);
//启动“游戏选项”的“控制器ID”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Joy.cpl,,1\',9);
//启动“鼠标”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Main.cpl\',9);
//启动“声音和多媒体”的“声音”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,0\',9);
//启动“声音和多媒体”的“音频”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,1\',9);
//启动“声音和多媒体”的“硬件”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,2\',9);
//启动“电话和调制解调器选项的”的“调制解调器”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Modem.cpl\',9);
//启动“扫描仪和照相机”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sticpl.cpl\',9);
//启动“系统”的“常规”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,0\',9);
//启动“系统”的“网络标识”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,1\',9);
//启动“系统”的“硬件”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,2\',9);
//启动“系统”的“用户配置文件”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,3\',9);
//启动“系统”的“高级”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,4\',9);
//启动“日期和时间”的功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL timedate.cpl\',9);
//启动“电源选项”的功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Powercfg.cpl\',9);
//启动“电话和调制解调器”的“拨号规则”功能
x:=winexec(\'rundll32.exe shell32.dll,Control_RunDLL Telephon.cpl\',9);
if x=0 then
messagebox(0,\'程序超出内存\',\'错误\',0);
if x=ERROR_BAD_FORMAT then
messagebox(0,\'该程序是一个非法的Win32程序).\',\'错误\',0);
if x=ERROR_FILE_NOT_FOUND then
messagebox(0,\'指定文件没找到\',\'错误\',0);
if x=ERROR_PATH_NOT_FOUND then
messagebox(0,\'指定路径没找到\',\'错误\',0);
end;
3. 如何实现对数据库中字段数据的模糊查询
在SQL的查询命令Select语法中,用LIKE可以使用两个很特殊的通配符“%”和“_”(下划线)其中百分比符号可以匹配任何长度的字符串,而下划线只能匹配单个字符,如果能在数据库查询设计中灵活使用这两个通配符,就可以对数据表中的字段进行模糊查询。例如,要查询学生基本信息表(jiben.dbf)中姓名(XM)字段所有“王”姓学生:
Select * From jiben where xm like ‘王%’
若需要查询“王”姓学生且其姓名只有两个字时,可使用下面的语句:
Select * From jiben where xm like ‘王_’
若要查询email地址字段,以Q或L开头的记录,可使用下面的语句:
Select * From jiben where email like ‘[ql]%’
需要说明的是,如要查询百分比符号或下划线字符本身,则需要使用方客户将它们括起来。
4. Delphi有哪些快捷键
快捷键的使用能加快程序开发速度,节省程序设计人员的时间。
n 对窗体中组件的操作
Ctrl+UP:向上移动当前组件(精确);
Ctrl+Left:向左移动当前组件(精确);
Ctrl+Down:向下移动当前组件(精确);
Ctrl+Right:向右移动当前组件(精确);
以上的快接键,再加上Shift进行组合,例如Ctrl+Shift+Right即可实现对组件位置的粗略调整。
Shift+UP:减小当前组件的高度;
Shift+Left:减小当前组件的宽度;
Shift+Down:增加当前组件的高度;
Shift+Right:增加当前组件的宽度;
n 对Object Inspector的操作
F11:切换到Object Inspector,若连续按F11则将实现在Object Inspector、Form和Code Editor之间的切换;
Ctrl+Down:下拉当前窗体的组件列表;
Ctrl+Enter:编辑带“…”的属性值(如组件的Font属性);
Alt+Down:下拉组件当前属性选项队列(如Align->alNone,alLeft,alRight等);
Ctrl+Tab:在属性列表及事件列表中切换。
5. 如何选择被组件覆盖了的窗体
当窗体中某个组件的Align属性设置为alClient时,此时选择窗体则变得较麻烦,事实上,可以通过使用以下的方式选择被组件覆盖了的窗体。
n 在Object TreeView中选择窗体;
n 按Esc键,一层一层选取,直到窗体被选中为止;
n 按Shift的同时单击鼠标左键,一步即可选中窗体;
n 按F11选定Object Inspector,然后切换到属性列表,再用Ctrl+Down快捷键。
6. 如何解决打开一个DBF数据表时出现的“Index not Found…”错误
当用户创建一个DBF表时,若使用了MDX格式的索引文件,那么DBF表的表头中的某个字节就自动设置了一个Flag。下次试图重新打开这个DBF表的时候,BDE会自动识别这个Flag,从而试图打开相应的MDX文件,若相应的MDX文件不存在,则会产生运行期错误。解决的方法是将Flag处的值置零。下面完成一个示范程序,选择
请发表评论