Delphi技巧必读

来源:互联网 发布:数据结构算法2.1 编辑:程序博客网 时间:2024/04/30 09:04
 
《Delphi开发者杂志(Delphi Developer's Journal)》董占山-南山古桃-Delphi必读(4)
2008年05月21日 星期三 下午 08:21

董占山

(中国农科院棉花研究所,河南安阳,455112)

I 在程序中启动其他程序的方法

使用ShellExecute函数(在单元shellapi中)可以执行一个程序或调入一个文件,不论这个文件是执行程序还是图象或文档等。函数的使用方法如下:

ShellExecute(Handle,'open',PChar(Edit1.Text),'','',SW_SHOWNORMAL);

ShellExecute(Handle,'open', 'c:/doc/bar.doc' ,'','',SW_SHOWNORMAL);

它的效果类似于在Windows资源管理器中双击了一个文件。如果执行函数成功,返回值就是 打开的应用程序例程的句柄,或者是DDE服务器应用程序的句柄。如果执行函数失败,返回值是一个小于等于32的错误号。将'open'换为 'print',这个函数就可以打印指定的文件了。

J 利用系统图象列表

假如你需要存取WIN95的系统图象列表,这里给出具体方法。第一个函数将系统图象列表的索引保存到一个特殊类型的文件中:

function GetFileIcoIndex(Filename:String):Integer;

var

Ext: String;

ShFileInfo: TSHFILEINFO;

begin

Ext := filename;

ShGetFileInfo(PChar(Ext), 0, SHFileInfo,

SizeOf(SHFileInfo), SHGFI_SMALLICON or

SHGFI_SYSICONINDEX or SHGFI_TYPENAME);

result:= SHFileInfo.iIcon;

end;

下面将系统图象列表连接到TListView控件上。注意我们设置动态建立的图象列表的ShareImages属性为真,这可以确保我们不试图释放Windows系统拥有的图象。在窗体的OnCreate事件处理程序中加上:

with YourListView do

begin

SmallImages := TImageList.CreateSize(16,16);

SmallImages.ShareImages := True;

SmallImages.Handle := ShGetFileInfo('*.*', 0,

      SHFileInfo, SizeOf(SHFileInfo), SHGFI_SMALLICON or

      SHGFI_ICON or SHGFI_SYSICONINDEX);

LargeImages := TImageList.Create(nil);

LargeImages.ShareImages := True;

LargeImages.Handle := ShGetFileInfo('*.*', 0,

      SHFileInfo, SizeOf(SHFileInfo), SHGFI_LARGEICON or

      SHGFI_ICON or SHGFI_SYSICONINDEX);

end;

关闭窗体时,在其OnDestroy事件处理程序中加上以下代码,释放申请的系统资源:

YourListView.SmallImages.Free;

YourListView.LargeImages.Free;

K 使你的窗体保留在桌面的最上面

当我们想让一个窗体保留在桌面的最上面时,可以定义窗体的FormStyle属性,使窗体保 持在最上面。但是,使用这种方法后,在切换窗体的模式时,窗体将闪烁。为了避免切换窗体模式时的闪烁,可以使用Windows API函数SetWindowPos来解决这一问题,使用方法如下:

SetWindowPos(Form1.handle, HWND_TOPMOST, Form1.Left, Form1.Top, Form1.Width, Form1.Height,0);

用实际窗体名称代替"Form1",调用这个命令就可以将窗体设置为保留在桌面的最上面。如要将窗体切换回正常的窗体,调用下面的命令:

SetWindowPos(Form1.handle, HWND_NOTOPMOST, Form1.Left, Form1.Top, Form1.Width, Form1.Height,0);

L 跟踪Windows的临时目录

Window 95和NT都使用一个目录来保存临时文件,这个临时目录不是固定不变的,为了确保应用程序使用正确的临时文件目录,可以使用Windows API函数GetTempPath来获取这个目录的路径,使用格式如下:

DWORD GetTempPath(DWORD nBufferLength, LPTSTR lpBuffer );

为了方便使用这个Windows API函数获取临时文件目录路径,编写了一个Object Pascal函数:

function GetTempDirectory: String;

var

TempDir: array[0..255] of Char;

begin

GetTempPath(255, @TempDir);

Result := StrPas(TempDir);

end;

使用GetTempPath可以获得TMP环境变量指明的路径;如果TMP没有定义,可以获得TEMP环境变量指明的路径;若TMP和TEMP都不存在,就使用当前目录作为临时目录。

M 处理自己的热键

应用程序可以使用许多Windows默认的热键。但是,有时需要向窗体添加自己的热键。当用户键入他们时,怎样捕获它们呢?为了解决这个问题,首先应将窗体的KeyPreview属性设置为True,然后在窗体的OnKeyDown事件处理程序中添加如下代码:

if (ssCtrl in Shift) and (chr(Key) in ['A', 'a']) then ShowMessage('Ctrl-A');

OnKeyDown事件处理程序将捕获击键,并执行指定的代码。

六、Object Pascal编程技巧

A 调用基类初始化例程的简便方法

假如编写一个衍生类的初始化例程,并希望将参数传递给基类的初始化例程,一般使用如下方法:

TMyButton.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

{ Do custom things... }

end;

但是,如果衍生类的初始化例程与基类有相同的参数,就不必明确地指出基类的初始化方法和参数,只需要按如下方法调用即可:

TMyButton.Create(AOwner: TComponent);

begin

inherited;

{ Do custom things... }

end;

B 安全的数组循环

一个Object Pascal数组是一组同类元素的有序集合。如果你不知道数据的上下界值,怎么来存取其中的元素呢?使用Low和High函数可以保证你存取其中的每个元素,请看下面的实例:

var

   MyArray : array[2..11] of integer;

   Position : integer;

begin

     for Position := Low(MyArray) to High(MyArray) do

         MyArray[Position] := 0;

end;

使用这项技术,代码中不包含任何立即数,程序代码就相对独立于数组的上下界区间。另外,由于这两个函数调用在编译时就转换为常数,故不存在任何执行代价。

C 确保字段有效

将验证字段有效性的代码放在关闭窗体的按钮的OnClick事件处理程序中是很自然的事情。但是,为了确保在任何情况下都能验证字段的有效性,需要使用窗体的OnCloseQuery事件。这样,无论怎样关闭程序,都能保证字段得到验证,并保证了代码的唯一性。

procedure TForm1.FormCloseQuery(Sender: TObject;

var CanClose: Boolean);

begin

CanClose := False;

if (Edit1.Text = '') then begin

        {错误处理}

        end

else

if (RadioGroup1.ItemIndex < 0) then begin

        {错误处理}

        end

else

        CanClose := True;

end;

在过程的起始处设置CanClose属性为False,确保OnCloseQuery不关闭窗体。然后,验证每一个字段并处理错误。最后,如果没有错误发生,设置CanClose为True。

D 快速定制程序的动态菜单

在编制应用程序时,我们有时需要根据用户的使用水平来动态地定制程序菜单,通常的做法是切换 菜单项目(TMenuItem)组件的Enabled属性,从而禁止或打开一个菜单项目,这样做起来十分复杂并且容易出错。其实,通过给每个菜单项目的 tag属性赋值,并根据用户的水平加以判断,就可以快速安全地定制程序菜单。方法如下:

1 按正常的方法建立菜单;

2 根据用户水平,给每个菜单项目的tag属性赋值,例如,将“文件”*“打开”和“文件”*“关闭”的属性设置为2,将“文件”*“新建”、“文件”*“保存”和“文件”*“打印”的tag属性设置为3;

3 在窗体的OnCreate事件处理程序中加上下列代码:

for i := 0 to MainMenu1.Items.Count -1 do

begin

    if UserLevel < MainMenu1.Items[i].Tag then

      MainMenu1.Items[i].Visible := False

    else

      MainMenu1.Items[i].Visible := True;

    for j := 0 to MainMenu1.Items[i].Count -1 do

      if UserLevel < MainMenu1.Items[i].Items[j].Tag then

        MainMenu1.Items[i].Items[j].Visible := False

      else

        MainMenu1.Items[i].Items[j].Visible := True;

end;

4 这段代码可以根据用户的使用水平动态地决定哪个菜单项目显示或哪个菜单项目不显示。

上述代码只能动态地开关一个菜单的菜单条目,如果要同时动态改变所有菜单的菜单条目,需要使用窗体的Components属性,代码如下:

for i := 0 to ComponentCount -1 do

    if Components[i] is TMenuItem then

        TMenuItem(Components[i]).Visible :=

          (UserLevel >= TMenuItem(Components[i]).Tag);

这段代码更加紧凑高效,是定制应用程序菜单的良好工具。

E 验证用户输入的日期

有经验的程序员知道不应该盲目接受用户输入的日期。诚然,你可以检查日期的年、月、日是否正确。但是,你检查日期是否实际存在?例如,假设用户输入9/31/97,月、日、年分别都是有效的,但是9月没有31号。为了检查一个日期字符串是否有效,可以使用如下代码:

var adatetime : tdatetime;

...

try

   adatetime:=StrToDate(inputdatestring);

except

   // EConvertError错误 - 无效的日期或日期格式

end;

这段代码对瑞年也同样有效。

F 建立全局条件定义

在编译程序时,用条件定义来决定包含或排除一块代码,由编译器决定哪块代码将是执行文件的一部分。通常的条件定义包括发送调试信息到一个文件和对Windows 95或Windows NT优化。下面是一个条件定义的实例:

unit Unit1;

{$DEFINE MYDEF1} //建立自己的条件定义

{$IFDEF MYDEF1} //如果宣布了条件定义

//更新标签的显示

Label1.Caption := 'MYDEF1 is declared.';

{$ENDIF}

有时,需要在一个项目的多个单元中使用同一个条件定义,可以采用下面的方法:

4         单击“Project”*“Options”,弹出Project Options对话窗口;

5         单击“Directories/Conditionals”标签;

6         在“Conditional Defines”编辑框中输入MYDEF1;

7         单击“Ok”按钮返回即可。

G 为TListBox组件添加水平滚动条

Delphi的TListBox组件会自动添加一个垂直滚动条,即当列表框的高度容纳不下所 有的列表条目时,垂直滚动条就自动显示。但是,当条目的宽度大于列表框的宽度时,水平滚动条不会自动显示。当然, 可以在列表框中加如水平滚动条,方法是在窗体的OnCreate事件处理程序中加入如下代码:

procedure TForm1.FormCreate(Sender: TObject);

var

i, MaxWidth: integer;

begin

MaxWidth := 0;

for i := 0 to ListBox1.Items.Count - 1 do

if MaxWidth < ListBox1.Canvas.TextWidth(ListBox1.Items.Strings[i]) then

    MaxWidth := ListBox1.Canvas.TextWidth(ListBox1.Items.Strings[i]);

SendMessage(ListBox1.Handle, LB_SETHORIZONTALEXTENT, MaxWidth+2, 0);

end;

这段代码先查找列表框中最长的条目的宽度(以象素点表示),然后, 用LB_SETHORIZONTALEXTENT消息来设置列表框的水平滚动条的宽度(以象素点表示),外加两个额外的象素。

H 用MessageDlg函数显示信息的技巧

Delphi提供了一种简单的方法显示信息对话窗口---使用MessageDlg函数在屏幕窗口中心显示一个对话窗口。MessageDlg函数的声明如下:

function MessageDlg(const Msg: string; AType: TMsgDlgType;

                  AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;

从其函数声明中,看不出它可以显示可变的信息。但并非如此,它可以显示可变信息,通过将变量连接到显示信息字符串中,而且可以在字符串中加入#13,使其后的信息在新的一行上显示。请看实例:

procedure TForm1.Button1Click(Sender: TObject);

var

ErrorDescr: string;

begin

ErrorDescr := 'Call 911';

   MessageDlg('Error Code: 123'+#13+ErrorDescr,mtInformation,[mbOk],0);

end;

I 动态删除TListView组件的列

如果曾经试着在运行时删除一个TListView组件的列,就知道它不允许这样做。真的不能吗?这里介绍一个文档中没有介绍的Delphi函数,使用它可以做到这一点。这个函数在CommCtrl单元中定义,函数声明如下:

function ListView_DeleteColumn(hwnd: HWND; iCol: Integer): Bool;

其中,hwnd是TListView组件的句柄,iCol是欲删除的列的索引号,注意索引号是从零开始计算的。下面给出一个使用实例:

procedure TForm1.Button1Click(Sender: TObject);

begin

   ListView_DeleteColumn(MyListView.Handle, 1);

end;

J 存取保护类属性

有时,需要存取在别处声明的保护类属性,最简单的方法是在自己的单元中使用下面的声明:

      TExposed<classname> = class(<classname>);

例如:

     TExposedWinControl = class(TWinControl);

因为TExposedbutton现在是在你的单元中声明的,所以你可以存取被保护的所有属性---包括继承下来的。怎么使用呢?使用实例如下:

if Sender is TWinControl then

with TExposedWinControl(Sender) do

begin

//Caption & Text在TWinControl中是一样的

ShowMessage(Caption);

Text := 'Easy, isn''t it?';

end;

K 在调试程序时使用Format代替IntToStr函数

如果你以前是一个Visual Basic用户,当显示调试信息时,你可能在MsgBox函数中使用Format$来显示变量,但是,在Delphi中, Format函数的工作方式相当不同,所以许多用户借助于下面的代码:

ShowMessage('Undocumented feature in ' + Application.EXEName +

     ' Variable now equal to ' + IntToStr(i_var));

为了使用Format函数, 需要知道使用哪个Format串。Format串看起来十分复杂,但是仅仅需要知道其中的3个,它们相当于占位符号,将会用Format函数的第二个参数中的变量来替代。这3个字符串是:

%d -- 代表一个整型数

%n -- 代表一个浮点数

%s -- 代表一个字符串

因此,上面的例子将改为:

ShowMessage(Format('Undocumented feature in %s. Variable is now equal to %d',

            [Application.EXEName,i_var]))

它们的结果是一样的,但是代码更加清晰并且容易修改。

当要显示大量变量时,Format函数就显示出其真正的威力,见下例:

ShowMessage(Format('V1=%d and F2=%n in routine %s',[V1,F2,'UNIT2.PAS']));

另外,用MessageBox代替MessageDlg或ShowMessage函数可以压缩EXE文件的大小,也可以使用Format函数做到这一点,例子如下:

Var sz:Array[0..255] of Char;

MessageBox(0,StrPCopy(sz,Format('V1=%d and F2=%n in routine %s',

     [V1,F2,'UNIT2.PAS'])),'DEBUG',64);

一旦你开始应用Delphi的Format函数,你就会越来越少地借助于IntToStr函数!
原创粉丝点击