Delphi编程技巧 窗口中状态栏的显示 许多软件为了与用户进行动态交流，需要时时向用户提供一些信息，使用户能随时了解当前状况。常用的一个方法是使用状态栏。状态行位于窗口下方，用来显示一些提示性文字，如当前打开的文件名称、当前打开的数据库名称、数据库记录数等。 1.在窗体中增加状态栏 在窗体中加入一个面板组件Panel1，其特性设置如下：
Align设为alBottom，状态栏总是位于窗体的下部。
Caption置为空。
Alignment设为taCenterCenter，显示文字居中。
Bevelinner设为bvLowered；Borderwidth设为2，产生具有粗线内外框且具有凹下效果的状态栏。
2.在状态栏中显示系统状态
给Panel1的标题（Caption）赋值以更新状态栏显示的内容。例如想知道单击“打开”按钮（Button 1）构件后，系统打开的文件及其大小，可在Button1的OnClick事件输入如下内容：
procedure TForm1.Button1Click（Sender：TObject）；
begin
Panel1.Caption：= 文件名： ＋Str1＋ 文件大小： ＋Str2
end；
其中：Str1和Str2均是字符串，Str1为文件名，Str2为文件大小。
3.在状态栏中显示各个构件的提示
Windows中有很多的软件使用即时提示，即当鼠标指定到某部件时，状态栏出现其提示；而当鼠标移走后，则该提示消失；Delphi工具同样可以完成上述功能，具体做法如下：
type
TForm1=class（TForm）
Button1：TButton；
Panel1：TPanel；
Edit1：TEdit；
procedure FormCreate（Sender：TObject）；
private
{Private declarations}
public
procedure DisplayHint（Sender：TObject）；
end；
var
Form1：TForm1；
implementation
{ ＄R ＊ ．FRM}
procedure TForm1.DisplayHint（Sender：TObject）；
begin
Panel1.Caption：=Application.Hint;
end；
procedure TForm1.FormCreate（Sender：TObject）；
begin
Application.OnHint：=DisplayHint；
end；
运行时只要Button1的Hint中有内容，当鼠标移动到Button1时，状态栏就出现Hint中的内容；而当鼠标移走后，状态栏中Hint的内容将消失。
巧用回车键
在Foxpro中，键入回车键后，光标就自动从一个输入框转到下一个输入框。但Delphi采用的方式与Windows完全一样，必须键入Tab键，光标才从一个输入框转入到下一个输入框。Delphi提供两种命令按钮，按钮组件（TButton）和图示按钮（TBitbtn）。假设在窗体Form1中有两个输入框（Edit1和Edit2），一个按钮组件（Button1）。键入Tab键，光标从输入框Edit1转入到下一个输入框Edit2。若想达到键入回车键，光标从输入框Edit1转入到下一个输入框Edit2，我们可以通过修改按钮组件的特性来完成。具体方法如下：
方法一：
(1)首先将按钮组件Button1的Default特性设置为True，这样在程序执行期间若有回车键键入，则Button1的OnClick事件被执行，在Button1的OnClick事件中编写程序，就可以达到从Edit1输入框转到Edit2输入框的目的。
(2)编写TForm1.Button1Click事件程序
procedure TForm1.Button1Click（Sender：TObject）；
begin
if Edit1.Focused then
Edit2.SetFocus；
end；
方法二：
(1)将按钮组件Button1的Default特性设置为True。
(2)编写Edit2.Focused子程序
procedure TForm1.Edit2.Focused（Sender：TObject）；
begin
Edit2.SetFocus；
end；
同时在Unit1中填写：
public
{Public declarations}
procedure Edit2.Focused；
(3)编写TForm1.Edit1Enter事件
procedure TForm1.Edit1Enter（Senter：TObject）；
begin
Button1.OnClick：=Edit2.Focused；
end；
这样，当键入回车键后，Button1的OnClick事件被响应，同时Button1的OnClick事件被定义为子程序Edit2.Focused，所以最终执行的程序是子程序Edit2.Focused，即Edit2输入框成为焦点。
在应用程序中制作背景音乐
下面列举两种在Delphi中较容易实现背景音乐的方法。
1. OLE控制
使用Delphi提供的“OLE接收框”组件，我们可以利用OLE嵌套的方法很快地为一个应用程序加入背景音乐，音乐文件可以是WAV文件或MIDI文件。
(1)进入Delphi系统环境，建立一个新的窗体Form1。选择组件板中的“系统组件（System）”选项卡，点击“OLEContainer”组件，并将其拖至Form1中任意位置建立一个OLE接收框（OLEContainerl）。在OLEContainer 1上点击鼠标右键，在快捷菜单中选择“插入对象（InsertObject…）”，然后在“插入对象”窗口中选择“媒体剪辑”，选中“以图标显示”栏（必须选中此栏，否则无法将其隐藏起来），按“确认”按钮退出。
(2)在“媒体播放机”窗口中，选择“文件”→“打开”，从文件列表中选择想要作为背景音乐的声音文件。由于音乐文件一般不是很长，所以要求循环播放，可以通过设置“媒体播放机”重复播放来实现。然后选中“重放的控制条”复选框，确认后退出。
(3)回到Delphi系统环境，在“对象监视器（ObjectIn
spector）”窗口中选择“OLEContainer1”，将它的Visible属性设为Flase（不可见），Autosize（自动调整大小）属性设为Flase；再选择Form1的“事件（Events）”标签，加入Form1的FormCreate过程：
procedure TForm1.FormCreate（Sender：TObject）；
begin
OLEContainer1.Dovelb（0）；
end；
(4)按下F9键即可运行此程序。
2.多媒体播放器（MediaPlayer）
使用Delphi提供的“多媒体播放器”组件，只需编写几句程序就可以为一个应用程序加入背景音乐，音乐文件可以是WAV文件或MIDI文件。
(1)进入Delphi系统环境，建立一个新的窗体Form1。选择组件板中的“系统组件(System)”选项卡，点击“MediaPlayer”组件，并将其拖至Form1中任意位置以建立一个多媒体播放器(MediaPlayer1)。设置它的Visible属性为Flase，DeviceType属性为dtAutoSelect(自动选择)，Filename属性为音乐文件的文件名(.WAV，.MID)。
(2)编写相应过程。
procedure TForm1.FormCreate（Sender：TObject）；
begin
MidiaPlayer1.Open；
MediaPlayer1.Play；
end；
procedure TForm1.MediaPlayer1Notify（Sender：TOb ject）；
begin
MediaPlayer1.Play；
end；
(3)按下F9键即可运行此程序，程序运行后会自动循环播放背景音乐。
加快应用程序的启动速度
如果程序的规模较大，有几十个窗体单元，那么最终编译好的程序的启动时间是较长的。可以使用两个办法来加快启动速度。
方法一：
在程序启动时对于一般不用的模块不进行建立。在程序需要调用时再自动生成，这样就加快了启动速度。
修改方法是在Delphi环境下选择菜单Project→Options,在Forms选项内将暂时不用的窗体从左侧移到右侧即可。
在程序运行到需要调用时不能只用Show方法，而要有创建窗体的语句，如：
Application.CreateForm(Tfrmprint,frmprint);
其中frmprint是一个打印模块例子。
这时可能会慢一点，因为启动的模块分散了。但是它也有一个好处：就是减少了系统资源的占用，因为不用的窗体不进行建立，窗体调用完毕自动释放。
方法二：
加入一个启动窗体SplashForm，在程序运行前显示，让用户不会有太长的等待的感觉，就像字处理软件Word的启动一样。
方法是在主启动模块.DPR文件中进行如下设置（在Delphi环境下按Alt+V，J即可见到。这个文件用户一般不必进行干预，Delphi会自动进行调整维护），其中SplashForm是启动窗体，它包含一些与程序有关的简单信息。
Application.Initialize;
SplashForm := TSplashForm.Create(Application);//建立启动窗体
SplashForm.Show; //显示启动窗体
SplashForm.Update ; //刷新启动窗体
Application.CreateForm(Tfrmall,frmall);//开始建立其他程序窗体，这些窗体如使用频率不多，可不必在此处建立
Application.CreateForm(Twaitwin, waitwin);
Application.CreateForm(Tfrmempty, frmempty);
{其它必要窗体的建立}
Application.CreateForm(Tappendrequer,
appendrequer);
SplashForm.Hide; //关闭启动窗体
SplashForm.Free; //释放内存，因为它在启动后将不再被使用
Application.Run; //显示主运行程序的窗体。
动态更改屏幕分辨率
有许多小工具可以在不重新启动Windows的条件下，动态更改屏幕分辨率。你是不是也想自己动手做一个呢？请在
interface段中加入下面一句：
function Resolution(X,Y:word):boolean；
然后在implementation段中写入如下代码：
function Resolution(X,Y:word):boolean；
var
DevMode:TDeviceMode;
begin
Result:=EnumDisplaySettings(nil,0,DevMode);
if Result then
begin
DevMode.dmFields:=DM_PELSWIDTH Or DM_PELSHEIGHT;
DevMode.dmPelsWidth:=X;
DevMode.dmPelsHeight:=Y;
Result:=ChangeDisplaySettings(DevMode,0) =DISP_
CHANGE_SUCCESSFUL;
end;
end;
接着，在form中放个button，caption为“800×600”，然后在其OnClick事件中写下
if Resolution(800,600) then ShowMessage('800×600模式!')；
好了，试一下吧！
把文件放入回收站
为了保险起见，我们通常把要删除的文件先放入回收站中，但是在Delphi中似乎没有这样现成的语句。如果你查询help，它只会告诉你用Deletefile函数会“不可挽回地”删除文件（真要恢复当然也可以办到，只是很麻烦）。那怎么办呢？别急，假设要安全地删除c:\temp.tmp文件，可用以下代码实现。
Var T:TSHFileOpStruct;
filename:String;
begin
filename:='c:\temp.tmp';
with T do
begin
Wnd:=0;
wFunc:=FO_DELETE;
pFrom:=Pchar(filename);
fFlags:=FOF_ALLOWUNDO
end;
try
SHFileOperation(T);
except
on eaccessviolation do showmessage('删除命令已被取消!');
end;
注意：如果指定文件不存在，或者在确认删除时按了“取消”，都会执行except后的语句。最后再提醒一句，别忘了在uses语句中加上shellapi。
屏蔽系统功能键
有时我们不希望程序被用户用Alt＋Tab、Ctrl＋Alt＋Del、Ctrl＋Esc等系统功能键打断（比如自动演示程序），那么该怎么做呢？提示你一点，在运行屏幕保护程序的时候，这些功能键都是不起作用的——也就是说，只要在你的程序运行过程中，“欺骗”一下Windows，使它自以为正处在屏幕保护状态就成了！请看下面这段代码：
var
temp : integer;
begin
SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, @temp, 0);
end；
当然，在程序结尾处千万别忘了恢复设置，把Windows“唤醒”。代码如下：
var
temp : integer;
begin
SystemParametersInfo( SPI_SCREENSAVERRUNNING, 0, @temp, 0);
end；
如何分行提示
Delphi中大部分控件都有一个实用的Hint属性，即浮动条提示。但有时提示较长，是否可以使得浮动提示条分行显示呢？其实，Hint是一个字符串（string），因而Delphi显示该字符串时会自动解释其中的回车控制符，所以只要加上回车控制符就可以了。依此原理，我们还能做出别具一格的垂直提示条。请先在form1中布置一个label，然后看示例代码：
procedure TForm1.FormCreate(Sender: TObject);
begin label1.Hint := ′垂′＋ ＃13 ＋ ′直′＋ ＃
13 ＋ ′提′＋ ＃13 ＋ ′示′；
end;
在菜单中显示图片
用过Office97吗？是不是觉得在菜单中显示图标很新鲜？如果想让你的程序也能如此锦上添花，那就请你赶快准备bmp吧！
假设你打算为“文件”菜单栏（name为n1）下的第9项（序号改为8）“打印”添加一个打印机图标（文件名为
c:\printer.bmp），那只要在form的OnCreate事件中这样编写：
var
Bmp : TPicture;
begin
Bmp := TPicture.Create;
Bmp.LoadFromFile(′c:\printer.bmp′);
SetMenuItemBitmaps(n1.Handle,8， MF_BYPOSITION,Bmp. Bitmap. Handle,Bmp.Bitmap.Handle);
end;
其中，第一个bitmap.handle用于未选定菜单项（unchecked），第二个则指定了选定（checked）时所显示的bitmap，同异皆可。另外，由于菜单项的高度有限，所以，若bitmap过大，只有左上角被显示。
获取当前行号
用RichEdit（或者memo）控件制作文本编辑器时，通过访问lines.count属性可以得到总行数，但是若想知道光标当前所在行的行号就麻烦了，因为Delphi没有提供这个属性。要实现这个编辑器必备功能，就须调用em_ LineFromChar。请试试下面的程序。
先在窗口中布置一个RichEdit或者memo（命名为editor），以及一个button。在button的onclick事件中写入下列代码。
var
CurrentLine:Integer;
begin
CurrentLine:=Editor.Perform(em_ LineFromChar,SFFFF,0);
Application.MessageBox(PChar(′当前行号是′＋IntToStr(CurrentLine)),′消息′,mb_ iconinformation);
end;
需要注意的是，第一行的行号为零。
利用TTable和TQuery的Filter属性
1.在设计时设置Filter属性
例如，设置Filter为：Country=′China′
然后改变Filtered属性为True（注意，Filtered为True时过滤才有效）。则只能看到对应的Country字段内容为‘China’的记录。
设置Filter时可以使用的操作符有：<、>、<=、>=、=、<>、AND、OR、NOT。
例如，设置Filter为：CustNo>=1000 and CustNo<=5000，则只能看到顾客号在1000与5000之间的顾客记录。
2.在程序运行期间进行动态过滤
要在程序运行时改变Filter属性，这包括两种情况：
(1)操作符右边为常量，例如：
Table1.Filter:=′State′＋′=′＋′′′HI′′′;
注意：字符串常量必须用三对单引号括起来。
(2)操作符右边不为常量，可能是通过一个变量指定的值，或由一输入框给出的值。这时需要用到Format函数。其代码形式为：
Table1.Filter:=Format(′State′＋′=′＋′′′％S′′′,[StateValue]);
其中StateValue为已经赋值的一个字符串变量，也可以为其他形式，例如:Edit1.Text。
用ApplyRange筛选数据集的记录
执行下面这段代码，将只能看到顾客号在1000至5000之间的顾客记录。组成该例程的几个过程为：ApplyRange、
SetRangeStart、SetRangeEnd。
Table1.SetRangeStart;
Table1[′CustNo′]:=1000;
Table1.SetRangeEnd;
Table1[′CustNo′]:=5000;
Table1.ApplyRange;
注意：该过程只适用于索引的字段。如果想基于非索引字段筛选，则不妨使用一点小花招：建立假索引。
实现的方法为：
Table1.IndexFieldNames:=字段名;
Delphi还提供了简单的调用SetRangeStart、SetRangeEnd和ApplyRange的方法，例如：Table1.SetRange([Edit1.Text]，[Edit20.Text])。
用OnFilterRecord事件筛选
OnFilterRecord事件允许按非键控字段建立筛选程序,例如：
procedure TForm1.Table1FilterRecord(DataSet:TDataSet;var Accept:Boolean);
begin
Accept:=DataSet[′State′]=′CA′;
end;
用TQuery控件的SQL语句
1.SQL语句中不包含变量和参数
Select＊from Customer
Where CustNo>=1000 and CustNo<=5000
2.SQL语句中包含参数
Select＊from Customer
Where CustNo>=:CustNo
在运行期间给参数CustNo赋值。
3.SQL语句中包含变量
这时向TQuery控件添加SQL语句的代码应当这样写：
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(Format(′Select＊from Customer′＋′′＋′where State=′＋′′′％S′′′,[StateValue]));
Query1.Open;
编写打印程序决窍
1.获取、显示当前打印机的分辨率
Windows下的打印机分辨率对打印程序有着至关重要的作用，如果你想知道打印机的分辨率，请在程序中加入一行：
ShowMessage(′水平分辨率′＋inttostr(GetDeviceCaps(printer.Handle,LOGPIXELSX))＋chr(13)＋′垂直分辨率:′＋inttostr(GetDeviceCaps(printer.Handle,LOGPIXELSY)));
2.将结果直接送到打印机
Delphi提供了两种打印方式：一是将结果输送到Form，再调用Form的print方法将结果输送到打印机，二是将结果直接输送到打印机。如果你采用第一种方式，则无论你怎样调整Form的PrintScal属性，打印出来的东西也不会让你满意。因此建议采用第二种方式。
3.尽量不要使用AssignPrn
尽管AssignPrn简化了文本打印操作，使输出到打印机像输出到文件一样简单。但简单带来的是一系列的不方便：你无法知道当前打印的行数，无法准确控制行距，无法灵活改变字体字形等等。还是用打印机的Canvas属性进行打印吧。
4.用打印机的点数做度量单位
如果想让打印程序在任何打印机上都能正常地打印，你就必须改变你的度量单位。如果采用固定的度量，不同分辨率的打印效果是不同的。举例来讲：printer.Canvas.rectangle(0,0,360,720)在360×360的佳能4200SP上能打出一个1英寸宽、2英寸高的矩形，但在600×600的惠普6L上只能打出0.6英寸宽、1.2英寸高的矩形。使用打印机的点数做为度量单位是一个明智的选择。具体做法如下：
Var PointX,PointY:integer;
PointX:=GetDeviceCaps(printer.Handle,LOGPIXELSX);
PointY:=GetDeviceCaps(printer.Handle,LOGPIXELSX);
printer.Canvas.rectangle(0,0,PointX＊1,PointY＊2)
这样，无论你使用什么样的打印机，都能得到一个1英寸宽、2英寸高的矩形。
5.添加打印程序单元
尽管Delphi在生成窗体时会自动在uses部分加入许多程序单元，但打印程序单元(Printers)却不在之列，要想使打印机正常工作和程序不出错，你还是老老实实手工给它加上吧。
(何俊锋）