# P. B- ~( ^7 CFillTreeNodes(0, NULL);//填充所有数据到各节点 ^) c, E& U. t6 T
$ G g# m) p8 Q4 L! _" ]8 A
} : f9 D. n2 t0 |1 f' B; R. o0 ]+ X/ m. v3 W+ M `% L; _) ~
else . k3 h6 o2 O# k7 N
8 i# Z4 u2 M/ m8 M& E9 G' ^{//如果指定值为false则 1 E2 Y0 C7 q& U; W( m( u. r4 D% h
1 R8 O9 b* {( h Y
ClearAllNodes(); //删除所有节点 1 E0 Z7 ~0 ^% E; F. Q0 Y: o+ ^- j6 b1 R6 W9 ^
} 9 n# h3 w$ L) a6 l# A: p! h$ T
FActive = Value; //将指定值赋给FActive 9 ]" @9 W6 Y+ v- K/ y# r* c) i4 C+ U* ?/ H
} 2 a* l, C! q# B, x) Z6 N' {" z4 E2 _) | N. Z
} 4 R5 {" s$ l1 J/ y8 p. M2 O0 t
& L" F9 H% i% |7 h- n' E, Y{//用指定值填充子节点 ( |7 N' Z- j+ D
" [3 B0 A) I" Y# e# [+ t& a0 nTQuery* AQuery = new TQuery(this);//创建一个数据集控件 " Z- L9 \5 |3 ^4 I4 _
2 w A) U6 a S' V# S0 x2 O
AnsiString strSql, strText; + u- j. C$ i! v) L
7 b% ^0 i* t3 I }, [+ v$ U2 LmmTalk->Lines->Add(Socket->ReceiveText()); Y& U4 s3 E/ _5 k; X- mint i=mmTalk->Lines->Count-1; & R, B+ Q& g. w1 b0 Fif(mmTalk->Lines->Strings==mmTalk->Lines->Strings[i-1]) - _9 o. E) h' [0 G
mmTalk->Lines->Delete(i); 3 v* B/ {: P7 i
; }; y: X. q, R" C/ R) i( L
公用部分 8 h* m; p1 e u! m当在edTalk输入交谈内容,按回车键表示输入完成,此时把交谈内容发送出去并清除edTalk的内容。在发送信息时,要看本程序是作为服务器还是客户机,如果是服务器则把信息发送到每一个客户;如果是作为客户则把信息发送到服务器。代码如下: 0 q/ [0 i1 p: B% |! d( m: ?
8 X. T) i8 W; y+ U" W0 y
if(Key==13) 9 @$ W, K9 q* ^: ^
{ 4 D& G$ l' w% r) i1 cmmTalk->Lines->Add(edName->Text+":"+edTalk->Text); ' g/ [% H- A4 |& a0 pif(ckListen->Enabled&&ckConnect->Enabled==false) / j' R" C$ q [3 A# e
//"监听"有效,"连接"无效。表示是服务器 - E1 p ^$ H3 |8 ]{ 6 \) m/ L+ B! H6 p# xint i; 0 |& P& b( U% E {% Q; v+ zfor(i=0;i<ServerSocket1->Socket->ActiveConnections;i++) 0 I/ r/ h! ]$ v5 j, I3 `ServerSocket1->Socket->Connections->SendText(edName->Text+":"+edTalk->Text); / S% d% M* W/ c/ M* J" S1 z: ^ e
} w& ^9 l: W+ ^, A" l2 ]% N0 H
else + J9 I4 C, ?5 R5 |{ ) u h- b( B& E( `7 Z+ X0 C, N/ T
ClientSocket1->Socket->SendText(edName->Text+":"+edTalk->Text); * m+ Q% b5 a& T: K
} 7 D- c2 l/ j- a1 J7 m
edTalk->Text=""; 5 Z0 M3 x' j3 f6 i3 y} " V) h; a( Q) E3 g ^6 E5 d4 c( } Z% C% {4 {) y9 e- fmmTalk的内容不可能永远增加,所以当它有100行时就清空它,在mmTalk的OnChange事件中检查: 4 K7 t0 a4 T( D7 @" ]5 b/ d3 C, \
( b" ^" K, r5 {' n% r' [$ q
if(mmTalk->Lines->Count>=100)mmTalk->Lines->Clear(); ( |& | f. G0 Q' g
4 R; H& X% H& u$ O
当然你也可以双击mmTalk来清空它,在mmTalk的OnDblClick事件中加入: 8 v5 h, v& \1 o- }3 C+ L! N8 ]' {; E/ S: O
mmTalk->Lines->Clear(); & m0 f% y) c' r当你觉得谈话的内容很有意思,你可以单击bbtSave打开保存对话框设置保存特性,所以在bbtSave的onClick中加入代码: + m( C% B. i, D
* K7 q c( p4 @/ O9 J' w# T$ Rif(sdTalk->Execute()) # f/ C% [2 U$ F* A
mmTalk->Lines->SaveToFile(sdTalk->FileName); 5 }1 X3 S' X `9 R
$ K! L6 o! y% U
我们的闲聊程序完成了,在局域网中试试吧?如果你只有一台机器,客户程序在连接时服务器时输入localhost或你机器的名字就可以了。 3 @9 a G; x6 C! W i7 N1 B$ s" u5 Y/ x" s. l$ ]6 z, S
(九)读写端口的两种技巧 - G6 W/ @/ g+ t( j, W2 ?, Y
3 S/ P2 G2 p& |! c4 E2 Z2 d1 ~+ q<a href="mailtnxyc_twz@163.com" target="_blank" >nxyc_twz@163.com</A> 6 ~( T8 Y* F( L3 T, @) a* g! D8 ~" k/ m. W3 v
在C++Builder中,不能够使用Turbo C中的outputb和inputb端口读写函数。但我们可以有另外两种办法实现这个功能。一种为内嵌汇编语言,另一种为使用__emit__函数。 2 ?/ L! n) t8 Y3 R( O" s Q( D! G, `/ Y0 g# M2 q. r* `. T
1 通过内嵌汇编语言实现端口的读写 ( f2 a1 `: f3 A. k% m; A' j5 D1 ?1 a5 e" V
在C++Builder中,汇编语句必须被包含在以关键字asm为起始的一对大括号中: 1 Y# I) m+ L9 k, G e6 j% L( ~3 K% masm { * w+ U! T B6 G. M7 {
' ~, E& V# K4 R; ~汇编语句1 8 _, d a$ V/ W( r
* v" L2 \0 I( ~, Q0 M, z% x…… . L" K- Z: P& S% r
4 V5 j, }: Y: E2 I% {' w; H0 _
} # V- P2 Z* f9 G* D: D# ~' z) o* X4 y2 `: W
利用内嵌汇编语言编制端口输出函数如下: / t6 I1 M* E- t) a h. u, |9 ^1 b1 V6 Q1 E e7 s5 L
void OutPort(unsigned short port,unsigned char value)//port参数为输出端口地址,value参数为输出值 0 ~, ^. F4 ~7 U" S
{ + V r r& t9 n
asm{ " }; T' L. K0 O) {mov dx , port //把端口地址送到处理器DX寄存器中 , {1 S* z9 r, g# S7 j% T1 u
mov al , value // 把value 送到处理器AL寄存器中 9 _4 |! |- q. s' H/ v
out dx , al // 把AL寄存器中的值送到端口 " ~% r; r* i7 O& l8 }
}; X5 X& `$ c3 a8 Q* d6 A ( \0 B. b! ?% _1 F' v; Q} ( z& l1 Y- o& v& J: ?
# P& O e0 |' U0 [% @8 W0 {% o
该函数将无符号字符型8位的数据value写入地址为port的端口上,port的数据类型是unsigned short ,16位无符号短整形。 & \9 S) ^" N9 O+ X* n) h. F
利用内嵌汇编语言编制端口输入函数如下: . L- _) K. J0 c H2 _; {9 c
8 w; a. }4 d- h3 M
unsigned char InPort(unsigned short port)//port参数为输入端口地址,返回为输入值 ! ^- K2 j w$ C0 m {9 v
{ 7 Z$ f3 v( {& _' w8 m# q
unsigned char value ; 5 c& v; a- l& S' J asm{ 2 B5 a d3 s6 e! X3 V/ m" H+ x! G/ s
mov dx , port // 把端口地址送到处理器DX寄存器中 & R" \8 A) ~( S0 C# \" N1 C8 S: G# \, |
in al, dx // 从DX指定端口中将一数据送到AL寄存器中 % d- x: R4 A+ l1 N( N( n
mov ind , value // 把AL寄存器中的值赋给value 9 h5 u1 { c# q" J# \2 d
}; 9 |6 e4 r: R8 r
return value; //返回端口数据 * B; b( b& n- L$ a; k1 U6 v} * Q. L. j/ t! @' M. g& G% k) [- D' F: U# G
函数InPort从地址为port的端口读入一个无符号8位的字符型数据,其其参数只一个,即端口号。返回的数据为unsigned char类型的,为从端口读取的值。 - [" [" x8 X* X& ?; [) ` x$ Z
: V2 q" E! q$ i, J$ Y. q$ e/ \" \+ j4 u
2 通过__emit__函数实现端口的读写 : v. F! U0 n8 v7 }$ g4 W/ y0 @# b : s: K, D0 O$ y q! S- N# i$ ___emit__ 函数一般极少用到。其用法如下: / d8 Z) A2 ~1 L0 V: ~. ], s 8 p2 |: P$ Y; ~% g$ N0 _' fvoid _ _emit_ _(argument, . . .); 6 O! u$ }+ x& u @4 Z( ?
6 O( l/ l- X: s6 x( |2 o; U
该函数为C++Builder 的一个内部函数,调用的参数为机器语言指令。它在编译的时侯,将机器语言指令直接嵌入目标码中,不必借助于汇编语言和汇编编译程序。 5 J4 w2 x' X; v" f) I" w
如果想使用__emit__ 函数,必须熟悉80x86处理器的机器语言指令。如果调用的参数是错误机器语言指令,则程序将非正常运行,并很容易导致死机。 ; N4 B7 j# o9 L- J! E& [, Y/ G. ^6 M: g! C- c. `" g* Q
利用__emit__函数编制端口输出函数如下: % r& c F! D8 k0 ]5 [ + f, q' x* {' _ u4 S) ]3 Ivoid OutPort(unsigned short port,unsigned char value)//port参数为输出端口地址,value参数为输出值 " Y0 `0 F7 Y7 H% r{ ! T! ^9 j7 b1 g5 ~ N0 T' F__emit__(0x8b,0x95,&port); // 把端口地址送到处理器EDX寄存器中 ( x9 N" Y4 T5 Y* j) z/ i __emit__(0x8a,0x85,&value); // 把value 送到处理器AL寄存器中 ! ~6 {% M7 d `8 j6 J2 J3 |9 j) K: [
__emit__(0x66,0xee); // 把AL寄存器中的值送到端口 # _) a; j* a' o9 l* G: r: F2 I2 p! c- d, R9 N
} ( | D, v/ q9 I9 @8 ^3 q0 Q/ z" ~
利用__emit__函数编制端口输入函数如下: : c3 s; W. Z" |
: d5 M0 U' e% r! y! l, N, D
unsigned char InPort(unsigned short port)//port参数为输入端口地址,返回为输入值 5 t) G7 r( \0 G% X8 L
{ . }( ~- [3 `3 [2 N% _2 b i& g
unsigned char value ; - R! _& d1 w0 _
__emit__(0x8b,0x95,&port) ; // 把端口地址送到处理器DX寄存器中 1 w! [/ Z2 y" l2 `& I, m1 Z- O7 e
__emit__(0x66,0xec); // 从DX指定端口中将一数据送到AL寄存器中 ! k9 z/ g2 L5 c2 V3 t+ m; G __emit__(0x88,0x85,&value); // 把AL寄存器中的值赋给value 3 k' j9 _" n5 \" ~5 ~5 Vreturn value; //返回端口数据 1 Y4 Q6 h" Q# E, v} ) Y- x W0 w+ _& ~' N
) y; [8 `5 O2 f# h l( q" J- i由这两种方法所编制的函数注释可以看出,它们每一句的功能都是一样的,只是一个是嵌入了汇编语言,另一个是直接使用机器语言。 ) d6 \% f3 L, j/ X' u+ v+ Y" d' T3 ]7 Y& F8 m- F. M
& P0 T( X8 \0 s5 S/ b. {/ }9 G
3 应用举例 * G9 ?6 T# v! @0 P2 r* j
' c& ~6 _9 L# F4 [; I
在C++Builder中,通过File/New Application菜单新建一工程。 ' x+ \5 d7 ^. o- E
# {8 m" f/ Q4 X8 t- n
在表单中加两个Button控件,Caption分别为“写端口”和“读端口”。 ) K* C/ \: ^2 F4 @
& j% k( f1 A' F/ u3 Q7 q/ `将第一种方法所编制的OutPort和InPort函数拷贝到表单的头文件中,并把这两个函数作为表单类的在表单类的公有成员函数。 # G& g: K, r. Z C
2 r" ^" g# s' a0 R
分别双击两个Button控件,产生OnClick事件函数。在单元文件的.cpp文件添加如下代码: & Y. E8 @/ U3 e. @- y p5 p: A/ K6 C; s" Y' s* O% }; k
//--------------------------------------------------------------------------- ; c/ x9 `7 G; x7 p6 a$ }9 P. @* k
$ ?2 M' B8 G; }9 K" j{ 6 `: h# F4 ?, j4 @OutPort(0x378,0x00);//向地址为378H的端口输出数据 " ^6 O5 ?+ @ z2 k* WOutPort(0x379,0x00);//向地址为379H的端口输出数据 7 j7 u9 m. Y# |4 F
} ! C. d, E4 q% h( S
' P& R `) ?" h2 P* I$ y! Z8 R( ?5 S
//--------------------------------------------------------------------------- . k% S- V8 V8 M7 E7 D
# b# _' G5 [1 N: p# _
void __fastcall TForm1::Button2Click(TObject *Sender) / s6 B/ P* c2 Y% u4 a# t4 ?) n/ D* d6 @5 {: T" \% f
{ 0 @6 @7 j0 E- `% F( d0 z
int value; - A% d% F7 g3 |# v+ ~( b9 @ O
value=InPort(0x37a);//从地址为37aH的端口读入数据 5 v' J4 @2 ]' V$ z# s} 2 J0 h7 a. l: L5 o" l- z
- ^1 H2 U# V6 S3 `1 F9 a
//--------------------------------------------------------------------------- 9 v3 m H* K# H+ R. }" }" w. ]$ N) x! {7 Y0 v3 v
, }8 Q- e( v4 s+ [) t& n5 H+ `1 S2 b z(十)如何实现控件数组 6 r; _" i- W9 A i) ]* l: X
2 ^0 ^- H/ [9 W. S9 P# d8 w! x, Q) \2 _9 D& _! @9 B% n: J
2 n0 n" D3 E* M! F. v! N& p
4 t+ v/ l" @) F# f& N8 G; u2 X在C++ Builder中,没有提供像VB中控件数组的功能,很令遗憾。经过一番琢摸,终于解决了这个问题。技巧不敢独享,奉献出来供大家交流。 7 N" v @) C% F1 N $ [/ I! n9 v- Y/ {- R. [7 V5 n1 g. i$ T1 T; I. Y4 g5 l. f4 c; O3 Z' B
在VB中,控件数组可以 : 6 k W: T# a, S% T* C
* c0 w) C; P# v% y {! {( ]4 t' B* ~8 l; P9 R& b
允许多个控件共享同一个事件句柄 5 a2 X' g Y. ~' `- u" c7 F j提供了运行期间增加一个控件的机制 ) j4 J% J: t) p& ]4 U
提供了一种方便的组合控件的方法。 : s$ Y/ d& q! i8 [& \: X- H0 g0 |% v! s4 K前两项在C++ Builder中早已实现,而且CB更有一个优点。即不同类型的控件可以使用相同的句柄(只需在相关控件的Object Inspector窗口中的EVENT事件设置即可)。 , N% u1 O N- E: L' k C++ Builder中使用了Tlist类对象来组合控件数组,与VB控件数组元素必须为同一类型控件相比较,C++ Builder中的Tlist类对象可以组合任意类型的控件而不必强求同一种类,这样就大大地方便了程序开发者。例如可以把在不同Panel面板控件上的所有控件组合为一个控件数组。 . h. h! I. R' h
在具体的开发实践中,我采用Tlist类对象创建、维护了多类型的控件数组。其实现原理与方法详见以下的原程序代码。实例程序实现了动态创建一个包含8个TEdit类型控件和4个TImage类型控件的控件数组,在程序运行中对所创建的控件进行修改、维护的功能。 w& B, K/ y Z# S% e5 d) }: d
1、创建一个新的工程文件(New Application),在Form1上放置两个TPanel类型的Panel1和Panel2,调整大小合适,再在窗体下方放置四个TButton类型Button1,Button2,Button3,Button4,设置控件属性如下:Button1->Caption="新建控件数组",Button2->Caption="改变控件位置", Button3->Caption="还原到原位置", Button4->Caption="退出";Button2->Enabled=false,Button3->Enabled=false。 " T: @& H! z" n5 a6 | 2、在文件Unit1.H中加入以下声明: % I) |* k; `7 d7 h7 | c0 M
class TForm1 : public TForm ~/ Y- k" e; H3 b, a { 9 y, W# h `- E published: // IDE-managed Components * |7 D7 Y8 l3 P
TPanel *Panel1; 7 F1 _/ I, e1 b8 Y0 ?& `
TPanel *Panel2; , f. A% R: @- ?: {7 Q, b: |! _2 I
TButton *Button1; % |5 e7 O" t& }: p( g* J TButton *Button2; + _( S0 Y. f$ U# N6 p
TButton *Button3; 7 ?' R: K0 v+ l9 M- b- F9 O
TButton *Button4; $ i# U4 T# p: I+ }) }6 Y- F private: // User declarations 7 G" O8 T W" e& b/ V TList *MyVCL; ; h* c& K( N. J" b$ a: A public: // User declarations 3 ~9 ~7 m* ~7 ?5 Q2 a8 S
__fastcall TForm1(TComponent* Owner); H4 d5 x/ _% k. W% O virtual __fastcall ~TForm1( ); 2 O* v# F4 o F" U& r
}; 2 R* c1 O6 Y6 a2 p 3、切换到工程的Form界面,双击工程的主界面Form,创建一个OnCreate事件句柄,在文件Unit1.CPP中加入以下代码: v+ G' b0 K) y
void __fastcall TForm1::FormCreate(TObject *Sender) 0 t. H8 t5 B: _: K9 t! a { + h/ D: ^* d. g/ Q# U8 ]* r8 T" {# V
MyVCL = new TList;//创建TList对象 , [$ A t. k2 q4 u) Z+ ^ } # _4 {. w- D6 K3 _
将TForm1析构函数加入到文件Unit1.CPP中: 5 c: M$ l) `* d2 h7 I/ Y __fastcall TForm1::~TForm1() 6 Z4 d9 x/ G0 O; Q5 n3 f7 z' g+ m
{ 9 Q5 D/ I$ [, v! i. c
delete MyVCL; //删除TList对象 2 `; k$ E+ N0 H9 i; s# ? } 0 D2 U5 N! O2 B$ [0 I3 G 4、双击标签(Caption)为"创建控件数组"的按钮,创建一个OnClick事件句柄,添加以下代码到OnClick事件句柄中: : k. y" ^: O* v; ?/ K
void __fastcall TForm1::Button1Click(TObject *Sender) " {, }! B$ z- G5 @9 t { 1 u! U* H S6 z9 {1 t+ A% Q
//创建新的控件,调整其位置,并加入到MyVcl(TList 类)之中 ( E- L1 K: L: D# o# z7 ?
int temptop=5; 1 `7 ?9 u" d$ t2 ~% a
for (int i=0;i<4;i++) , _# B7 x' ?3 r$ T. [# g- ^8 m; I1 i& u. u6 u, i( |
{ 2 @0 ]1 ]( b0 F D7 A0 A' a& y w2 Q
TEdit *EditNow = new TEdit(this); 6 ~* B6 i7 v/ m! X) o, h
EditNow->arent=Panel1; # r; n' H7 T- w4 F# {
EditNow->Text= IntToStr(i); + i1 {7 ~, l+ G2 l1 q Q EditNow->ReadOnly=true; ( H0 J& N2 z a a P ^; O
EditNow->Top=temptop; ) t: r5 v9 S& I% z
EditNow->Height=24; " w$ J8 a1 d: A, ^
EditNow->Width=24; . k% B* r* i2 O; q2 w EditNow->Left=10; + f' F+ d3 H8 C( O; s- o4 S MyVCL->Add(EditNow); //加入到控件数组中 ) O5 L* |$ @0 c$ ] TImage *ImageOff= new TImage(this); ; _( a" H, G/ C, Y4 d8 \4 x- Q
ImageOff->arent=Panel1; ; n' h+ b {4 d' D r ]/ ] ImageOff->icture->LoadFromFile("None.BMP"); ( L ]5 @' Z! I+ R/ v
ImageOff->Top=temptop; & _2 R- l# f1 s1 P1 `# I
ImageOff->Height=24; 2 v9 {) R) I, D2 w6 T1 R
ImageOff->Width=24; ! ^" _& |: v, n! L/ Y+ a$ Z7 {
ImageOff->Left=EditNow->Left+EditNow->Width; ( C$ b: Y& `: D( ~5 S- ~0 s) p MyVCL->Add(ImageOff); //加入到控件数组中 / s7 q( n) R4 b& p
TEdit *EditStatus = new TEdit(this); ( J' e+ [( d) D9 E
EditStatus->arent=Panel1; " ]% q7 b4 i* T* a, W J, M5 } EditStatus->Font->Name = "Arial"; * a7 H$ m2 v l* t
EditStatus->Font->Size = 12; , N# g Q6 R6 x0 m5 Q: p% b! z
EditStatus->Text="禁止访问"; ! K/ X. c) z& D/ I8 f$ Q EditStatus->ReadOnly=true; V& M5 ?( |& E [; l- ~
EditStatus->Top=temptop; ! d6 u ~) W# f. ~) x: a1 g0 q6 P EditStatus->Height=24; - d5 l# k) }4 l) W+ U+ T" e EditStatus->Width=80; 2 l" F4 N. a) w& G S! C5 l4 F& R EditStatus->Left= ImageOff->Left+ImageOff->Width; / F# L* Q& C: \ MyVCL->Add(EditStatus); //加入到控件数组中 & h& j$ N# |6 c, i- g. r
temptop=temptop+24+5; + \1 v; l6 [" `; W4 @5 _ } & J7 D( @ f4 n
Button1->Enabled=false; 4 O8 M3 v1 u1 K, ^4 x- {: E4 e/ w
Button2->Enabled=true; ; n; n8 ~& J; l9 G; ~ } ) B. c& g$ r* R( I, Q: G
5、同4所示方法,依次双击标签的标题(Caption)为"改变控件位置"、 "还原到原位置"、 "退出"的按钮,创建对应的OnClick事件句柄,添加以下代码到对应的OnClick事件句柄中: 8 b% c) ]- A1 S* \
void __fastcall TForm1::Button2Click(TObject *Sender) & Q! i: ^: D0 t( V4 t { 1 i( c7 S# ?9 H% k2 _! e for (int i=0;i<4;i++) 4 h, n) x) D* e/ I8 m7 q
((TImage*)MyVCL->Items[i*3+1])->arent=Panel2; ( e9 p) e+ m" d' s
Button2->Enabled=false; 1 ^+ ~' j% o5 ~* H
Button3->Enabled=true; & a5 u% B" U/ \) Y3 H' t8 z } 1 P6 t( l+ T# L5 d void __fastcall TForm1::Button3Click(TObject *Sender) ' e( B* x/ P) l; h7 o% K+ s { 1 Q9 E4 E+ ^& H
for (int i=0;i<4;i++) 5 [ d/ \4 S/ l1 {$ o ((TImage*)MyVCL->Items[i*3+1])->arent=Panel1; 4 i5 o/ I( F l
Button3->Enabled=false; ! B$ H2 g( ` Z, w5 B Button2->Enabled=true; 9 E7 G' Z% p2 j E8 i3 Q& r } % R+ ?- l/ c% ~, b- { c
void __fastcall TForm1::Button4Click(TObject *Sender) 2 q" ^$ D) x T+ m2 ?, f5 Q
{ $ Y$ I \( {- {( Z p5 K
Close(); //关闭窗体 : s( k6 q* g6 U& f c# F6 @6 Q9 l
} - k. }! w8 \1 m! ]1 d- ]
由上所述,实际的代码大多只是用于设定控件位置和基本属性,真正实现控件数组功能的代码并不太多,也不复杂,而且十分的灵活;需要注意的是使用TList类对象组合中的各项控件之前,必须先将其强制转换为一个对象指针以指明其类型,才能对其属性进行修改/赋值操作。 & E& Q# g+ `, @5 e. m & l( n3 L! t! d5 T(十)用Sender参数实现代码重用 ; I: H4 w3 y8 u. A9 p- p& G, f& B. l$ X7 B0 R; E6 g1 B& w
<a href="mailtnxyc_twz@163.com" target="_blank" >nxyc_twz@163.com</A> ) P0 ^# N) Z) N2 W
# z& _- J7 U8 F; K x% D) H面向对象的编程工具的特点之一就是要提高代码重用性(Reuse),BCB当然可以实现这一功能。我们都知道,在BCB中,大部分程序代码都直接或间接的对应着一个事件,此程序称为事件处理句柄,它实际上就是一个过程。从应用程序的工程到窗口、组件和程序,BCB强调的是其开发过程中每一层次的重用性,可以充分利用已编写过的代码来减少工作量,更会使你的程序变得优美。代码段间的共享都跟发生该事件的控件有关,需要根据控件类型做出相应的处理,这时就要用到Sender参数。 # p. S9 q2 A. p. A J9 W7 t# u. P8 U* _8 w) \! d
每个函数的开头都有形如: , w/ w6 [2 Q5 K nvoid _fastcall Tform1::Button1Click(TObject *Sender) % f2 b1 b* u9 f$ J0 ~+ a
3 E( C; c* k6 N( S
其中的Sender是一个Tobject类型的参数,它告诉BCB哪个控件接收到这个事件并调用相应的处理过程。我们可以编写一个单一的事件处理句柄,通过Sender参数和if语句或者case语句配合,来处理多个组件。在Delphi中可以用IS来测试Sender类型,或者用AS进行类型转换,BCB我们只在用dynamic_cast来进行上面两个工作,下面把dynamic_cast的用法说明一下。 " v# X3 g/ ~/ U: R. X. Z+ I 5 N6 |$ m. t1 B6 Zdynamic_cast 可以把某种对象强制转成另一个类,类型转换成功则返回一个值是0的指针,失败则丢出一个异常处理信息:Bad_cast,但你放心不会导致系统死机,所以可以放心使用。其程式: / W# h1 f9 M% ddynamic_cast <T> (ptr) 9 ? K$ ]; o, f9 [# ?& u, Z: K# C2 g: U
T参数一定要是一个指针、void 、或是已经定义过的类,而ptr参数则必须是一个指针(pointer) 或是一个引用(reference)。如果T的类型是void,那么ptr则是一个可以访问最下面类里的任何成员,当然这样的类将不可以是基础类。 0 f4 C3 }; \2 Q) K " f+ u% U) c \' q) U4 q1.进行判断 % \& j; P) \# X- i
我们用dynamic_case来测试Sender,以便找到调用这个事件的处理句柄或组件的类型。如,并窗口编辑框和标签的Click事件的处理句柄都指向窗口的xxx函数,编辑框和标签对Click事件有不同的反应: 6 H2 u4 @/ T6 [: n4 E7 `8 P
void _fastcall TForm1::xxx(Tobject *Sender) ! m" {; j5 E- Q: B/ E{ ( m, S1 \& B, m- Z+ i$ Qif(dynamic_cast<TEdit *>(Sender) * @# p' E* r$ j- c( \
showmessage(“This is a editbox”); + i7 `( p: `& d1 W
if(dynamic_cast<TLabel *>(Sender) . x) N+ l/ o2 U3 w4 c/ M! I$ t( o2 @showmessage(“This is a label”); ' y" w }6 o1 l$ Y. R: q
} - Q# k8 e6 h3 Q! z ; A/ {9 _0 S1 f4 W2 H8 Y2.强制进行类型转换 0 b1 K5 ]) E: B( ]. y( `. D将若干继承同一父类的子类强制转换成该父类。如窗口中有一个TEdit类控件和一个TMemo控件,它们实际上都继承于TCustomEdit类,如果你要为二者的某一事件提供同样的处理,可以将二者事件句柄都指向自定义的函数yyy: + m6 d5 M, [% W& a8 rvoid _fastcall TForm1::yyy(Tobject *Sender) # ?& p$ b/ O; s& _{ 8 q$ A W. o3 `+ c$ U# \4 V6 {dynamic_cast<TCustomEdit *>(Sender).text=”This is some demo text”; 9 B! N" b1 o7 }" M
} 3 ]: v! l8 d9 U5 I5 K- i" n2 o在这里,先把TEdit类和TMemo类均强制转换成TCustomEdit类,再对其父类的属性进行赋值。 : |! L; o: u$ n! U' y7 T
使用Sender参数可以通过单一函数段处理多类组件,真正体现了BCB的面向对象的重用性。</DIV>