delphi 技巧合輯
INI文件的操作要点* G8 J1 E2 k3 d% Z, m) i
(1)在interface关键字后的uses子句中加入* \! G& x, t1 S. f, U
uses …,inifiles;9 l* }. n6 n) g* m5 Z- p3 g
(2)设置变量:var inifile:=TIniFile;
( K, B1 g9 m& k' e, X+ b(3)使用前,一定要先做:
8 z/ B9 z+ O. cinifile:=TIniFile.Create(inifilename);1 [; p- x) m" Y! g
我在这里绕了个弯子,写成
- q& C4 G& t* T6 H2 _$ O& N* X1 Xinifile.Create(inifilename);* E9 D+ [5 q. {# ]3 g2 C2 G
; R! ~ u( G+ u- u$ Y# I
TShiftState用来检测CTRL,ALT,Shift,以及鼠标按键的状态,
" Z! c1 x- U& p- b常常用在键盘或鼠标的事件处理程序之中。
* v' Z& v: [) y H/ n/ F5 F+ x* T. g* B: d
DCU文件(编译的库单元)的重用
1 y' z1 s1 M6 T# Y(1)在interface里的uses子句中引用。如Windows,Dialogs,( k9 v4 m4 P# r! D/ ]* W
等等,要求被引用的DCU一定要放在Delphi 3\Lib子目录中。
; \% x0 d5 Z+ M$ S0 ?5 o) H(2)在implementation里的uses子句中引用。如果是自行编写的
/ L' h7 P0 N# b/ |5 M& [* L9 ^DCU文件,应采用这一方式,并将被引用的DCU文件放入当前+ _1 |; s/ Y9 Y- x! m! l; U
工程所在的子目录中。
' g2 C! h+ @+ `1 _0 s; x3 x. N: Qexecutefile函数(在FmxUtils.DCU中定义)不能用在FormCreate$ Z+ G9 L: B# f$ {
事件中。真奇怪……
: o U7 N: \# }8 \+ b: x$ r/ r% s6 H( ?, x3 s: D6 g
内置的版本号控制功能
/ C# ]3 [/ e# ^5 _: M" \在Project Options中的VersionInfo项里,有版本信息的定制,2 B* w8 q9 G7 W! _9 S; e
包括一个有趣的版本号自动增加功能(Auto-increment build
% I0 _7 X& t# j' r7 J6 U/ {number),即每Build一次,版本号中的Build Number加1。选中' B' d" N# b, V; E, R& t; ]
Include version information in project,编译,用资源管. W* ]& r7 J4 a9 t0 ]3 m& V
理器里找到生成的.exe文件,点右键,查看“属性”一项,就; P0 ?" d$ s- j2 g! ^: U6 z& F! T. o- ?
会看到在属性中多了一页“版本”,里面就是你刚才输入的各; N& \ |' c+ R
项内容。& r; a4 H1 a; j
g/ K7 c- g& W {8 Z% o3 S
& W8 W( m' C8 a: k0 Y& n定制Speedbar$ I" b l4 j0 k3 }0 |* w% I" `
右键点击左上角的加速键条(speedbar),选中Properties,
# D! e3 Y+ L* l+ V在列表框中找到你想要加入speedbar的命令按钮,将它拖到$ H) ]8 v6 q9 {
speedbar上的合适位置即可。如果speedbar上没有多余的空间
7 H4 H4 K% u' ^( a1 j$ q! D2 @来容纳新的按钮,可把鼠标在speedbar右框处变为双向箭头的" f. L. ?) N% r4 Q2 p
形状,向右拖动一段距离,就可腾出空间。如想删除speedbar
/ O0 ~1 a/ @' V0 A1 i& v: o3 v: x上的某个按钮,可简单地把它拖出speedbar的范围,就能删除。
: G2 I6 Y1 q; H6 v8 ^$ e
% Q P f1 g( r* R0 a# F6 D# U' R5 h) K$ q' q! X, ?
计算出的字段
0 O' j' C( X/ r6 l( V0 t: B在写一个程序时,我要在DBGrid中显示出一个memo的部分信息,当我把那个% M6 ~( ^- o* C5 |/ T* q1 s' L
memo字段放进去后,运行时只能显示出“(memo)”字样。从书上找到计算字
% r$ k1 d6 Y$ Z7 T7 F段的用法后,我在TTable中增加一个计算字段MemoHeader,宽度为40,在此
$ M+ s, X _. ]1 i4 f- p9 f' GTTable的OnCalcFields事件中输入:7 @0 C8 m' S, K( m6 U
with MyTable do
% ~* Q& T. N& l: mFieldByName(‘memoheader’).asstring:=FieldByName(‘memo’).asstring;9 v3 }7 H. p# Z" l3 |5 l
在DBGrid中放入MemoHeader,运行,OK!
% v" y; z* D5 [0 S" L0 L7 \+ f$ t: o
, C! L. b- ?/ B0 P4 d3 V& l* v字段编辑器
% E' P: \; G6 k! Z解决了上面的问题后,我继续学习字段编辑器的作用。原来,DELPHI通过字8 x5 ?# h& O& D& p, D6 M
段编辑器,近乎完美地实现了数据库的字段级的控制,这包括:
: [% b7 \) [4 {7 W5 I! j: ~( G, l1)字段在DBGrid中标题、宽度、能否可见;' c# f. h8 N( ?& t
2)字段的显示格式和编辑时输入的过滤条件;
$ y! c/ f. Z5 q8 q# g3)字段是否只读;' _" B' j1 @4 C
4)字段的类型:数据字段/计算字段/查询字段/内部计算字段;
7 _' A9 S3 j1 I, ^. h" v' {5)字段的显示/赋值/修改等事件的定制。
9 V2 f+ S# \1 t& k6 j5 \8 H% P( W9 t+ e
& C! Q3 L* A$ A, q
设计Form时的快捷键8 P, @6 n. j- x9 I) x% u
Del 删除所选组件2 g. d3 O p. `: A" C4 M7 s
Esc 选择当前组件的容器(通常是Panel,Group或Form)! A! w. a! _! V9 u" q; z7 h
F11 在Form或Unit和对象检查器之间切换
5 ~5 y8 s; ~" x6 nF12 在Form和代码编辑器间切换- h0 ^: o% J1 P+ Z
Ctrl-F12 显示“查看Unit”对话框
2 C7 k4 p8 b* q0 ?Shift-F12 显示“查看Form”对话框( ~1 ?- X8 W( h+ ]! _
TAB 下一个组件
/ v8 N& p. E, E* [( v6 ^, u- gShift-TAB 上一个组件$ M9 x& |' ?& v$ N" e: ~0 M/ S$ x
方向键 选中此方向上最近的组件# l/ r- d/ h4 A) u. V, |% U
Ctrl-方向键 将所选组件移动一个点
; n" A- Q9 U! V" G0 e- ~9 e) aShift-方向键 将所选组件改变一个点的大小7 a1 w4 k1 H0 h$ q( W$ {% A
Ctrl-Shift-方向键 将所选组件移动一个格& |. J7 [- v2 r( N5 h, e
Shift-click 按住Shift键再用鼠标点击组件,可以选择多个组件。" Z6 A) G; ^3 K& O* M$ u
–= 下面这条是我摸索出来的 =–
- a( \( ^7 \% S* \. N- f. {Ctrl-drag 按住Ctrl键在一个容器组件(如Panel,QReport,Groupbox等)/ d5 G! x5 j& |
里拖动鼠标,可强制选择鼠标拖过的矩形框中的所有属于此容6 _$ `/ q$ j* v! e a- b
器的可视组件但不包括该容器组件。
8 |% n' r' [' n/ `' e+ M) O* Y$ ?8 f
! s; f ~! e' _8 a
使用QReport的若干经验
! @" o$ ^3 F, |* e2 ]& D/ K1)QuickRep1.DataSet:=query1) D/ k; w9 U+ V+ D2 h' G3 Z: f
不然无输出结果: G e# f: m" d
2)从qrpt2man.doc中查知,格式化输出结果的方法是:: \( h* C3 B- Y0 d* S/ M" d
在QRDBText.Mask中设为.00;0,将使输出带两位小数。+ G0 Z% R5 w" n2 @3 h
3)设置Group时,先加入一个QGroup,再加入一个QRBand,将QGroup的" e c. }8 L, B
FooterBand设为刚才的QRBand,就可将其联系起来。" w. ^; f! d$ B" v( P
4)在Group中打印的合计数,其ResetAfterPrint设为True,可实现分组合/ V; W# K2 \& ~1 e# ]+ b
; G/ X% r: B _& p, b
format(‘%*.*f’,[8,2,123.456]); //等价于format(‘%*8.2f’,[123.456])
/ v! Q# I4 Z, T `! b6 D- p# q0 `format(‘%d %d %0:d %d’[10,20]);! z" T2 Z! z" T2 J- I" A
/ O1 ?6 W; C8 m1 l
! N( y. c- O9 j: Q0 E* s
' _3 W2 S) E- _: h6 }0 N: h; v' Q, h播放一个wav文件 e" k, S" e S" U, X" J; F' B% }. [
use mmsystem;: Q# M* Q: E; E- [ |( a
SndPlaySound(‘hello.wav’,SND_FILENAME or SND_SYNC), w' V' e/ ~* R! r# W5 {
0 P1 _% `! P' d! b2 [% |( H
使你的form适应不同的显示分辨率
' N1 v4 s2 I' y o" C假设你在800*600的分辨率下设计的form,第一步:
1 K: P8 W% v% U0 ~! C+ ~inplementation0 a5 H/ m4 p. F& U8 l5 k% p
const
: u9 M" ~- t) j' H, fScreenWidth: LongInt = 800; {I designed my form in 800×600 mode.}/ G6 g3 G0 S" w$ j
ScreenHeight: LongInt = 600;6 z5 ^; \9 J1 R
6 b1 E" Z9 q2 ]- n{$R *.DFM}+ b2 D; s0 W$ J3 v4 S
. |# x: _2 i A; r
procedure TForm1.FormCreate(Sender: TObject);
: }1 N2 ?2 a7 e: e& c# O+ ibegin
- _8 f- I3 z8 j* c _, Xscaled := true;
# `$ w0 c& y2 n8 D- j' G( k, I! Xif (screen.width <> ScreenWidth) then" p1 J6 p- O. B* ^# r- B v
begin
- j7 E' O- }: c8 Pheight := longint(height) * longint(screen.height) div ScreenHeight;
2 g9 A2 }2 C% s" e/ Vwidth := longint(width) * longint(screen.width) div ScreenWidth;
) x; u( w# `% T7 ~' P5 NscaleBy(screen.width, ScreenWidth);
$ B; c4 w j% o- c, Dend;; g0 D; z4 g# X7 [/ E" F o$ x% }
end;( T" g; `2 _) t$ n
- e5 J5 r% `- U
下一步,要让每个子控制的字体改变到合适的大小:4 F" v) Y- f$ X6 y, V2 n. A
type
& p& m% s1 [4 s4 j RTFooClass = class(TControl); { needed to get at protected }
, \: Q% y! @8 V; g9 q$ q{ font property }
/ n9 k1 u, u$ ?" q* ~6 {7 `' Y2 {7 t$ g8 n4 z# n
var
; |# C, [* s* Ai: integer;
$ l2 a2 X) B/ w: M0 f4 J. l: x5 Ebegin
! {" d: F% e& e* ffor i := ControlCount – 1 downto 0 do
7 P! }/ [7 E. dTFooClass(Controls).Font.Size := T+ n# q# D9 d2 Z
(NewFormWidth div OldFormWidth) *+ `5 Y) w( v6 R' P
TFooClass(Controls).Font.Size;% H; T& k5 u4 y: m8 P" m) s
end;
0 ^$ Z$ f/ ?: b3 u9 s5 B5 |6 n0 d5 B: p2 P
用raise引发一个错误处理例程
4 K y* _ j) G0 H2 R示例:先给出下面的定义6 L0 s) ?8 B8 J) k7 R! H Z% `
type EPasswordInvalid=class(Exception);" I2 J; d/ o( q6 ^6 r7 s1 w3 w
这样你就可以随时引发一个”password invalid”的例外:3 J: F' ]8 _4 q* Z
if Password<>CorrectPassword then: Y5 a& X* L( S" U* ^5 X
raise EPasswordInvalid.Create(‘Increect Password entered’);
6 Y- G7 B5 m& ~/ H; ^, m运行后,出现信息框4 u& j( t: z1 ]4 C
Project Project1.exe raise exception class EPasswordInvalid# ]' v5 m* M* b/ {# f9 o2 E" f
with message ‘Incorrect Password entered’. Process stopped.4 D- ?: E5 H5 s$ K8 w" J
Use step or run to continue.
/ P9 F5 p% O. y6 u* s/ c* q再按Run按钮,出现一个信息框:
! d, v, Y( ^! C6 l4 R) oIncorrect Password entered.
9 y' Q5 H* z$ y$ d# h- z$ m# p2 M% d4 `
( V- `/ r' C8 J" c6 q% v2 q1 Z. X% u
从DEMO例程中学到的若干小技巧0 x% H. `6 [6 v% @" q
在demo\internet\http\webbrows\main.pas中,
+ d4 Q9 P- d% P; N7 N# Fprocedure TForm1.DocumentSourceClick(Sender:Tobject);
2 D- X' V. ^; }begin4 D7 t1 X# |: {
with DocSourceForm do
. v9 I# u8 ~" zbegin' ~2 N& ]3 R7 `0 U+ E) k
show; // 显示HTML源码的FORM
- D0 M$ u5 |# rMemo1.lines.clear; // 清空memo1中内容% I$ I" g. }9 ^
Memo1.lines.add(HTML1.SourceText); // 植入源码2 `2 [7 w1 m9 R% [. r# D
Memo1.SelStart:=0 // ***" V: _7 h4 ?7 J( |1 F
SendMessage(Memo1.Handle, // Memo1的窗口句柄
, p; r- o2 v1 D! N+ Q/ x# v4 S$ HEM_ScrollCaret //将Edit Control中的caret光标滚动到视野中
, I; j- L" ]; ]4 }' [+ V! c0,0 //这两个参数暂不使用
6 ~* x# ^" G! pend;) ~% e, V( W* s; J
end;
- Z0 w8 _: [" e* v' F(此SendMessage系一WinAPI调用,给一个或一组窗口发送消息,直到 ^4 w- F6 |2 {# P
相应窗口处理此消息后才有返回值。相似的另一函数为PostMessage,
5 p( E' V/ e8 L- M7 k. f它将消息放入消息队列中后立即返回。)6 C! U( n2 A9 t1 ^# R, ?6 i% |
*** 清空当前选择并将光标移动到指定字符(0代表第一个字符)。如同时
7 N1 P7 }8 f7 I t7 d设memo1.sellength为一非0值,可选择一段文本。4 I% ~. ~ @$ O/ I$ q3 E/ g0 _
5 o+ c7 L- K+ Y( p* F0 C. M- h- E) f+ l$ A) L
奇怪的I/O error — 我在开发过程中发现下面这两条语句& V% o# V. x- N+ r6 {2 t. |
assignfile(fDesc,CDDatadir+describefile);
2 l+ J8 Y) V) |1 v1 S7 hreset(fDesc);
) O, }% q2 i7 J! D5 K每当我第一次运行到这里时,总会出现 I/O Error 183 的错误.
0 ^0 z0 h( S9 Z0 W最后我想这也有可能是没有清除 ioresult 的原因.在两句之间加入
7 e; C( l; z" i$ Z6 Kif ioresult<>09 b- Y" e3 [% L# y
then;
, l8 t w' A( ?0 toooook! 可以了.1 L5 H9 J5 x( j* q1 P4 W, I7 F( g6 Y I
) t" \+ f! W/ D" m
$ `" Y, G8 Y9 W7 P, m5 O0 i, D值得注意的I/O error. f8 R* \# o; U8 k1 A8 }
AssignFile(F, fname);5 A1 b* ?' }' a3 d, V# z
Rewrite(F);/ U2 _6 ]9 v" z) \, r. n
当文件 F 经 Reset(F)打开的情况下, Rewrite(F)会引发一个异常: I/O error 32 k1 \1 ?7 @- B+ c
反之,经 Rewrite(F)打开, Reset(F)也会引发同一个异常.
8 n g7 @5 i9 a: \Reset(F)两次,closefile(F),再Rewrite(F),或反之,也会引发同样异常." H U( g( n/ m! |2 {* H. P& I
, X5 N0 Z5 ~' l1 F& q& U
6 ?0 o: w1 n ~, _( D( ^# v# L! a# X* d) s1 F
string和pchar的相互转换:, }% F p) U& b6 b& P: M/ b( T
string –> pchar: pchar(s
* q/ X+ J# v" S, w5 H+ x" ^tr:string);" t0 Q) ~; x# J
pchar –> string: strpas(p:pchar);4 x. L# \: w) \* u
* O* C. u1 l" G" H# t$ L* a. ^4 s
9 B% X' q- }8 T6 q1 f* _
/ G) O, d6 _: u在Delphi代码中定义数据库
$ E3 ~' t; \! Vprocedure tform1.create_tbl;
6 V' F% F' b. U' l$ O9 d5 Rvar tbl:ttable;/ }) ^9 P: o- s$ {- d
begin
8 D) O# h- K V( ]) {tbl:=ttable.create(self); // 如没有声明中的”tform1.”,Delphi不认这个self6 \; `, t6 h" e* y- e* i
with tbl do
5 X. V c% ^6 b# V8 F4 }begin
A9 A. K5 U' bdatebasename:=;dbdemos’;2 V2 J( u6 O# g- F6 H: p
tablename:=’my_tbl’;
6 I$ d1 c4 I, D9 |- ?$ V5 ^- P9 Y' Rwith fielddefs do4 \# S7 |( [* n y8 M
begin
! E; p1 `4 W' G) dclear;
* v; A! D, f; l# Fadd(‘No’,ftInteger,0,false);
$ D; g7 i6 j+ \+ v( @. gadd(‘Name’,ftString,0,false);
5 l0 W; B+ e, |+ W: _2 T…# ?3 \* y# H. Q( p
end;4 E" h0 A' T0 y- l7 \
with indexdefs do4 C. K3 e3 G, D: {# ^) n g& E
begin
* M1 ]5 M9 j: F: o/ ?clear;0 a% t" f) m5 x B( V: t2 _$ u
add(‘primary’,'no’,[ixPrimary,ixUnique]);/ r6 c' J _! F
…" S6 D2 D8 O$ q; z$ _; }
end;
3 {$ `( o, T1 W$ G+ S* |+ xCreateTable;
& [9 k* ^1 x. z. P( m' s$ E9 send;
3 e' K- \- p1 D5 S( I. ]. c7 mend;+ ?) z$ b" A7 t* M4 ?* v
+ U! Q: J: Y4 k5 `
* Q/ J U7 `5 ^) _8 f
string、TStrings、pchar的相互转换(这里是我的个人经验)- ^3 _; p: B% b- X- G" b& L
假设有如下定义:( p" E2 [5 G7 M- s4 @
var
6 K7 G, M* B* V+ [. }/ E# _p:pchar;
5 y% R9 e1 T; is:string;8 A0 R# @/ E. F7 j5 @; |8 N ]
ss:tstrings;( T1 V }) `6 T' V6 e" I% L
begin4 R9 _- Y" V; ~+ S, ]
ss:=tstringlist.create; // 开始时一定不要忘记创建ss6 J- ^. S% C: E1 \! w- i0 F& U
ss.text:=s; // string –> tstrings d6 n4 F4 J$ }2 W& e3 i
s:=ss.text; // tstrings –> string" X; s7 V% \! v: D
p:=pchar(s); // string –> pchar
" [ H9 A$ ]$ e% }s:=p; // pchar –> string
, x. e! \' k8 J8 y' z0 fshowmessage(s); // 合法语句
! j6 u: Y8 J# _" t8 s" ashowmessage(p); // 合法语句9 ]: d9 N: B. v% o2 f
… …. |8 Y \, Z$ i3 g3 f
ss.free; // 最后还要记着释放ss占用的资源
/ q& _. c+ r \5 Mend;$ v# c* _4 E- X5 U. X3 T: H/ m, W
' s- a4 h. O* m( R6 Y& f* z
6 P. U) u* t- y2 @; V) u
8 ?, b0 C4 H; R8 y N+ s7 U2 C: F在设计Menu时,常用的ShortCut可从下拉菜单中选取。但我发现可选的ShortCut有些不在列表中,如ESC,Ctrl-Shift-A等。我试着在ShortCut项的右边输入:ESC,竟然成功了。再试Ctrl-Shift-A,这回Delphi自动把它变为Shift-Ctrl-A