﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-喜++&amp;&amp;方块糖-文章分类-Object Pascal/VCL/Delphi/Kylix</title><link>http://www.cppblog.com/WangYu/category/6129.html</link><description>海阔天空</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 23:35:32 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 23:35:32 GMT</pubDate><ttl>60</ttl><item><title>StringGrid</title><link>http://www.cppblog.com/WangYu/articles/42754.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Fri, 15 Feb 2008 00:58:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42754.html</guid><description><![CDATA[<h2>Delphi StringGrid使用全书1</h2>
<br>
blueski推荐&nbsp;[2006-12-9]<br>
出处：Blog - 风之谷<br>
作者：hottey<br>
&nbsp;<font><br><br>
</font>
<p style="font-size: 9pt;">
</p>
<p><font>StringGrid行列的增加和删除<br>type<br>TExCell = class(TStringGrid)</font></p>
<p><font>public<br>procedure DeleteRow(ARow: Longint);<br>procedure DeleteColumn(ACol: Longint);<br>procedure InsertRow(ARow: LongInt);<br>procedure InsertColumn(ACol: LongInt);<br>end;</font></p>
<p><font>procedure TExCell.InsertColumn(ACol: Integer);<br>begin<br>ColCount :=ColCount +1;<br>MoveColumn(ColCount-1, ACol);<br>end;</font></p>
<p><font>procedure TExCell.InsertRow(ARow: Integer);<br>begin<br>RowCount :=RowCount +1;<br>MoveRow(RowCount-1, ARow);<br>end;</font></p>
<p><font>procedure TExCell.DeleteColumn(ACol: Longint);<br>begin<br>MoveColumn(ACol, ColCount -1);<br>ColCount := ColCount - 1;<br>end;</font></p>
<p><font>procedure TExCell.DeleteRow(ARow: Longint);<br>begin<br>MoveRow(ARow, RowCount - 1);<br>RowCount := RowCount - 1;<br>end; </font></p>
<p>
</p>
<p><font><br>如何编写使StringGrid中的一列具有Check功能，和CheckBox效果一样<br>unit Unit1;</font></p>
<p><font>interface</font></p>
<p><font>uses<br>Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids;</font></p>
<p><font>type<br>TForm1 = class(TForm)<br>grid: TStringGrid;<br>procedure FormCreate(Sender: TObject);<br>procedure gridDrawCell(Sender: TObject; ACol, ARow: Integer;<br>Rect: TRect; State: TGridDrawState);<br>procedure gridClick(Sender: TObject);</font></p>
<p><font>private<br>{ Private declarations }</font></p>
<p><font>public<br>{ Public declarations }</font></p>
<p><font>end;</font></p>
<p><font>var<br>Form1: TForm1;<br>fcheck,fnocheck:tbitmap;</font></p>
<p><font>implementation</font></p>
<p><font>{$R *.DFM}</font></p>
<p><font>procedure TForm1.FormCreate(Sender: TObject);<br>var<br>i:SmallInt;<br>bmp:TBitmap;<br>begin<br>FCheck:= TBitmap.Create;<br>FNoCheck:= TBitmap.Create;<br>bmp:= TBitmap.create;<br>try<br>　 bmp.handle := LoadBitmap( 0, PChar(OBM_CHECKBOXES ));<br>　 With FNoCheck Do Begin<br>　　 width := bmp.width div 4;<br>　　 height := bmp.height div 3;<br>　　 canvas.copyrect( canvas.cliprect, bmp.canvas, canvas.cliprect );<br>　 End;<br>With FCheck Do Begin<br>　 width := bmp.width div 4;<br>　 height := bmp.height div 3;<br>　 canvas.copyrect(canvas.cliprect, bmp.canvas, rect( width, 0, 2*width, height ));<br>End;<br>finally<br>　 bmp.free<br>end;<br>end;</font></p>
<p><font>procedure TForm1.gridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);<br>begin<br>if not (gdFixed in State) then<br>　 with TStringGrid(Sender).Canvas do<br>begin<br>　 brush.Color:=clWindow;<br>　 FillRect(Rect);<br>　 if Grid.Cells[ACol,ARow]='yes' then<br>　　 Draw( (rect.right + rect.left - FCheck.width) div 2, (rect.bottom + rect.top - FCheck.height) div 2, FCheck )<br>　 else<br>　　 Draw( (rect.right + rect.left - FCheck.width) div 2, (rect.bottom + rect.top - FCheck.height) div 2, FNoCheck );<br>end;<br>end;</font></p>
<p><font>procedure TForm1.gridClick(Sender: TObject);<br>begin<br>if grid.Cells[grid.col,grid.row]='yes' then<br>　 grid.Cells[grid.col,grid.row]:='no'<br>else<br>　 grid.Cells[grid.col,grid.row]:='yes';<br>end;</font></p>
<p><font>end. </font></p>
<p>
</p>
<p><font>StringGrid组件Cells内容分行显示在Tstringgrid.ondrawcell事件中</font></p>
<p><font>DrawText(StringGrid1.Canvas.Handle,pchar(StringGrid1.Cells[Acol,Arow]),Length(StringGrid1.Cells[Acol,Arow]),Rect,DT_WORDBREAK
or DT_LEFT);</font></p>
<p><font>可以实现文字换行！ </font></p>
<p>
</p>
<p><font>在StringGrid怎样制作只读的列在 OnSelectCell事件处理程序中,加入: (所有的列均设成可修改的)</font></p>
<p><font><br>if Col mod 2 = 0 then<br>　 grd.Options := grd.Options + [goEditing]<br>else<br>　 grd.Options := grd.Options - [goEditing]; </font></p>
<p>
</p>
<p><font>stringgrid从文本读入的问题（Save/Load a TStringGrid to/from a file?）stringgrid从文本读入的问题</font></p>
<p><font>// Save a TStringGrid to a file<br>procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName);<br>var<br>f: TextFile;<br>i, k: Integer;<br>begin<br>AssignFile(f, FileName);<br>Rewrite(f);<br>with StringGrid do<br>begin<br>　 // Write number of Columns/Rows<br>　 Writeln(f, ColCount);<br>　 Writeln(f, RowCount);<br>　 // loop through cells<br>　 for i := 0 to ColCount - 1 do<br>　　 for k := 0 to RowCount - 1 do<br>　　　 Writeln(F, Cells[i, k]);<br>end;<br>CloseFile(F);<br>end;</font></p>
<p><font>// Load a TStringGrid from a file<br>procedure LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName);<br>var<br>f: TextFile;<br>iTmp, i, k: Integer;<br>strTemp: String;<br>begin<br>AssignFile(f, FileName);<br>Reset(f);<br>with StringGrid do<br>begin<br>　 // Get number of columns<br>　 Readln(f, iTmp);<br>　 ColCount := iTmp;<br>　 // Get number of rows<br>　 Readln(f, iTmp);<br>　 RowCount := iTmp;<br>　 // loop through cells &amp; fill in values<br>　 for i := 0 to ColCount - 1 do<br>　　 for k := 0 to RowCount - 1 do<br>　　 begin<br>　　　 Readln(f, strTemp);<br>　　　 Cells[i, k] := strTemp;<br>　　 end;<br>　 end;<br>CloseFile(f);<br>end;</font></p>
<p><font>// Save StringGrid1 to 'c:.txt':<br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>SaveStringGrid(StringGrid1, 'c:.txt');<br>end;</font></p>
<p><font>// Load StringGrid1 from 'c:.txt':<br>procedure TForm1.Button2Click(Sender: TObject);<br>begin<br>LoadStringGrid(StringGrid1, 'c:.txt');<br>end;</font></p>
<p><font>*******************************************</font></p>
<p><font>打开一个已有的文本文件,并将内容放到stringgrid中,文本行与stringgrid行一致; <br>在文本中遇到空格则放入下一cells.<br>搞定！注意，我只写了一个空格间隔的，你自己修改一下splitstring可以用多个空格分隔！</font></p>
<p><font>procedure TForm1.Button1Click(Sender: TObject);<br>var<br>aa,bb:tstringlist;<br>i:integer;<br>begin<br>aa:=tstringlist.Create;<br>bb:=tstringlist.Create;<br>aa.LoadFromFile('c:.txt');<br>for i:=0 to aa.Count-1 do<br>begin<br>　 bb:=SplitString(aa.Strings[i],' ');<br>　 stringgrid1.Rows[i]:=bb;<br>end;<br>aa.Free;<br>bb.Free;<br>end;</font></p>
<p><font>其中splitstring为：</font></p>
<p><font>function SplitString(const source,ch:string):tstringlist;<br>var<br>temp:string;<br>i:integer;<br>begin<br>result:=tstringlist.Create;<br>temp:=source;<br>i:=pos(ch,source);<br>while i&lt;&gt;0 do<br>begin<br>　 result.Add(copy(temp,0,i-1));<br>　 delete(temp,1,i);<br>　 i:=pos(ch,temp);<br>end;<br>result.Add(temp);<br>end;</font></p>
<p>
</p>
<p><font>StringGrid组件Cells内容对齐</font></p>
<p><font>在StringGrid的DrawCell事件中添加类似的代码就可以了：</font></p>
<p><font>VAR<br>vCol, vRow : LongInt;<br>begin<br>vCol := ACol; vRow := ARow;<br>WITH Sender AS TStringGrid, Canvas DO<br>　 IF vCol = 2 THEN BEGIN ///对于第2列设置为右对齐<br>　　 SetTextAlign(Handle, TA_RIGHT);<br>　　 FillRect(Rect);<br>　　 TextRect(Rect, Rect.RIGHT-2, Rect.Top+2, Cells[vCol, vRow]);<br>　 END;<br>end; </font></p>
<p>
</p>
<p><font>当我将StringGird的options属性中包含goRowSelect项时每当我选中StringGrid中一行，
则选中行用深蓝色显示，我想将深蓝色改为其他颜色应怎样该？当我将StringGird的options属性中包含goRowSelect项时每当我选中
StringGrid中一行， 则选中行用深蓝色显示，我想将深蓝色改为其他颜色应怎样该？<br>procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;<br>Rect: TRect; State: TGridDrawState);<br>begin<br>With StringGrid1 do<br>begin<br>　 If　(ARow= Krow) and not (acol = 0) then<br>　 begin<br>　　　Canvas.Brush.Color :=clYellow;// ClBlue;<br>　　　Canvas.FillRect(Rect);<br>　　　Canvas.font.color:=ClBlack;<br>　　　Canvas.TextOut(rect.left , rect.top, cells[acol, arow]);<br>　 end;<br>end;<br>end;</font></p>
<p><font>procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol,<br>ARow: Integer; var CanSelect: Boolean);<br>begin<br>krow := Arow;　//*<br>kcol := Acol;<br>end;　</font></p>
<p><font>注意：必须把变量KROW的值初始为1或其他不为0的值，否则如果锁定第一行的话，第一行的颜色将被自设颜色取代，而锁定行不会被重画。</font></p>
<p>
</p>
<p><font>怎么改变StringGrid控件某一列的背景和某一列的只读属性,StringGrid控件标题栏的对齐.怎么改变StringGrid控件某一列的背景和某一列的只读属性,StringGrid控件标题栏的对齐.<br>请参考以下代码：<br>在OnDrawCell事件中处理背景色。程序如下：<br>//将第二列背景变为红色。<br>procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;<br>Rect: TRect; State: TGridDrawState);<br>begin<br>if not((acol=1) and (arow&gt;=stringgrid1.fixedrows)) then exit;<br>with stringgrid1 do<br>begin<br>　 canvas.Brush.color:=clRed;<br>　 canvas.FillRect(Rect);<br>　 canvas.TextOut(rect.left+2,rect.top+2,cells[acol,arow])<br>end;<br>end;</font></p>
<p><font>//加入如下代码,那么StringGrid的第四列就只读了.其他列非只读<br>procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);<br>begin<br>with StringGrid1 do begin<br>　 if ACol = 4 then<br>　　 Options := Options - [goEditing]<br>　 else Options := Options + [goEditing];<br>end;</font></p>
<p><font>procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);<br>var<br>dx,dy:byte;<br>begin<br>if (acol = 4) and not (arow = 0) then<br>　 with stringgrid1 do<br>　 begin<br>　　 canvas.Brush.color := clYellow;<br>　　 canvas.FillRect(Rect);<br>　　 canvas.font.color := clblue;<br>　　 dx:=2;//调整此值，控制字在网格中显示的水平位置<br>　　 dy:=2;//调整此值，控制字在网格中显示的垂直位置<br>　　 canvas.TextOut(rect.left+dx , rect.top+dy , cells[acol, arow]);<br>　 end;<br>//控制标题栏的对齐<br>if (arow = 0) then<br>　 with stringgrid1 do<br>　 begin<br>　　 canvas.Brush.color := clbtnface;<br>　　 canvas.FillRect(Rect);<br>　　 dx := 12; //调整此值，控制字在网格中显示的水平位置<br>　　 dy := 5; //调整此值，控制字在网格中显示的垂直位置<br>　　 canvas.TextOut(rect.left + dx, rect.top + dy, cells[acol, arow]);<br>　 end;<br>end;　 </font></p>
<p>
</p>
<p><font>在stringGrid中使用回车键模拟TAB键切换单元格的功能实现......procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);<br>label<br>nexttab;<br>begin<br>if key=#13 then<br>begin<br>　 key:=#0;<br>　 nexttab:<br>　 if (stringgrid1.Col　　 begin<br>　　　 stringgrid1.Col:=stringgrid1.Col+1;<br>　　 end<br>　 else<br>　 begin<br>　　 if stringgrid1.Row&gt;=stringgrid1.RowCount-1 then<br>　　　 stringgrid1.RowCount:=stringgrid1.rowCount+1;<br>　　 stringgrid1.Row:=stringgrid1.Row+1;<br>　　 stringgrid1.Col:=0;<br>　　 goto nexttab;<br>　 end;<br>end;<br>end;<br>.........　 </font></p>
<p>
</p>
<p><font>stringgrid如何清空<br>with StringGrid1 do for I := 0 to ColCount - 1 do Cols[I].Clear; </font></p>
<p>
</p>
<p><font>选中某单元格,然后在该单元格中修改-&gt; 选中某单元格,然后在该单元格中修改设置属性:<br>　 StringGrid1.Options:=StringGrid1.Options+[goEditing]; </font></p>
<p>
</p>
<p><font>让记录在StringGrid中分页显示在Uses中加入： ADOInt <br>//首先设定PageSize，取出PageCount<br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>ADoquery1.Recordset.PageSize :=spinedit1.Value;<br>Edit1.Text := IntToStr(ADoquery1.Recordset.PageCount);<br>ShowData(spinedit2.Value);<br>end; </font></p>
<p><font>//然后将AbsolutePage的数据乾坤大挪移到StringGrid1中 <br>procedure TForm1.ShowData(page:integer);<br>var<br>iRow, iCol, iCount : Integer;<br>rs : ADOInt.Recordset;<br>begin<br>ADoquery1.Recordset.AbsolutePage:=Page;<br>Currpage:=page;　<br>iRow := 0;<br>iCol := 1;<br>stringgrid1.Cells[iCol, iRow] := 'FixedCol1';<br>Inc(iCol);<br>stringgrid1.Cells[iCol, iRow] := 'FixedCol2';<br>Inc(iRow);<br>Dec(iCol);<br>rs := adoquery1.Recordset;<br>for iCount := 1 to SpinEdit1.Value do <br>begin<br>　 stringgrid1.Cells[iCol, iRow] := rs.Fields.Get_Item('FieldName1').Value;<br>　 Inc(iCol);<br>　 stringgrid1.Cells[iCol, iRow] := rs.Fields.Get_Item('FieldName1').Value;<br>　 Inc(iRow);<br>　 Dec(iCol);<br>　 rs.MoveNext;<br>end;<br>　<br>//上一页 <br>procedure TForm1.Button2Click(Sender: TObject);<br>begin<br>If (CurrPage)&lt;&gt;1 then<br>　 ShowData(CurrPage-1);<br>end;</font></p>
<p><font>//下一页<br>procedure TForm1.Button3Click(Sender: TObject);<br>begin<br>If CurrPage&lt;&gt;ADoquery1.Recordset.PageCount then<br>　 ShowData(CurrPage+1);<br>end; </font></p>
<p>
</p>
<p><font>打印StringGrid的程序源码这段代码没有看懂，但是可能有的朋友需要，所以共享一下子 ：）<br>procedure TForm1.SpeedButton11Click(Sender: TObject);<br>Var<br>Index_R ,ALeft: Integer;<br>Index : Integer;<br>begin<br>StringGrid_File('D:\AAA.TXT');<br>if Not LinkTextFile then<br>begin<br>　 ShowMessage('失败');<br>　 Exit;<br>end;<br>//<br>QuickRep1.DataSet := ADOTable1;<br>Index_R := ReSize(StringGrid1.Width);<br>ALeft := 13;<br>Create_Title(TitleBand1,ALeft,24,HeaderControl1.Sections.Items[0].Width,20,<br>　　HeaderControl1.Sections[0].Text,taLeftJustify);<br>with Create_QRDBText(DetailBand1,ALeft,8,StringGrid1.ColWidths[0],20,<br>　　　　StringGrid1.Font,taLeftJustify) do<br>begin<br>　 DataSet := ADOTable1;<br>　 DataField := ADOTable1.Fields[0].DisplayName;<br>end;<br>ALeft := ALeft + StringGrid1.ColWidths[0] * Index_R + Index_R;<br>For Index := 1 to ADOTable1.FieldCount - 1 do<br>begin<br>　 Create_VLine(TitleBand1,ALeft - 13,16,1,40);<br>　 Create_Title(TitleBand1,ALeft,24,HeaderControl1.Sections.Items[Index].Width,20,<br>　　 HeaderControl1.Sections[Index].Text,taLeftJustify);<br>　 Create_VLine(DetailBand1,ALeft - 13,-1,1,31);<br>　 with Create_QRDBText(DetailBand1,ALeft ,8,StringGrid1.ColWidths[Index] * Index_R,20,<br>　　　　StringGrid1.Font,taLeftJustify) do<br>　 begin<br>　　 DataSet := ADOTable1;<br>　　 DataField := ADOTable1.Fields[Index].DisplayName;<br>　 end;<br>　 ALeft := ALeft + StringGrid1.ColWidths[Index] *　Index_R + Index_R;<br>end;<br>QuickRep1.Preview;<br>end;</font></p>
<p><font>function TForm1.ReSize(AGridWidth: Integer): Integer;<br>begin<br>Result := Trunc(718 / AGridWidth);<br>end;</font></p>
<p><font>function TForm1.StringGrid_File(AFileName: String): Boolean;<br>var<br>StrValue : String;<br>Index : Integer;<br>ACol , ARow : Integer;<br>AFileValue : System.TextFile;<br>begin<br>StrValue := '';<br>Try<br>　 AssignFile(AFileValue , AFileName);<br>　 ReWrite(AFileValue);<br>　 StrValue := HeaderControl1.Sections[0].Text;<br>　 For Index := 1 to HeaderControl1.Sections.Count - 1 do<br>　　 StrValue := StrValue + ',' + HeaderControl1.Sections[Index].Text;<br>　 Writeln(AFileValue,StrValue);<br>　 StrValue := '';<br>　 For　ARow := 0 To StringGrid1.RowCount - 1 do<br>　 begin<br>　　 StrValue := '';<br>　　 StrValue := StringGrid1.Cells[0,ARow];<br>　　 For ACol := 1 To StringGrid1.ColCount - 1 do<br>　　 begin<br>　　　 StrValue := StrValue + ', ' + StringGrid1.Cells[ACol,ARow];<br>　　 end;<br>　　 Writeln(AFileValue,StrValue);<br>　 end;<br>Finally<br>　 CloseFile(AFileValue);<br>end;<br>end;</font></p>
<p><font>function TForm1.LinkTextfile: Boolean;<br>begin<br>Result := False;<br>with ADOTable1 do<br>begin<br>　 {ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;' +<br>　　　　　　　　　　　 'Data Source= D:\;Extended Properties=Text;' +<br>　　　　　　　　　　　 'Persist Security Info=False';<br>　 TableName := 'AAA#TXT';<br>　 Open;　　　 }<br>　 if Active then<br>　　 Result := True;<br>end;<br>end;</font></p>
<p><font>function TForm1.Create_QRDBText(Sender: TWinControl; ALeft, ATop, AWidth,<br>AHight: Integer; AFont: TFont; AAlignMent: TAlignment): TQRDBText;<br>var<br>AQRDBText : TQRDBText;<br>begin<br>AQRDBText := TQRDBText.Create(Nil);<br>with AQRDBText do<br>begin<br>　 Parent := Sender;<br>　 Left := ALeft;<br>　 Top := ATop;<br>　 Width := AWidth;<br>　 Height := AHight;<br>　 AlignMent := AAlignMent;<br>　 Font.Assign(AFont);<br>end;<br>Result := AQRDBText;<br>end;</font></p>
<p><font>function TForm1.Create_VLine(Sender: TWinControl; ALeft, ATop, AWidth,<br>AHight: Integer): TQRShape;<br>var<br>AQRShapeV : TQRShape;<br>begin<br>AQRShapeV := TQRShape.Create(Nil);<br>with AQRShapeV do<br>begin<br>　 Parent := Sender;<br>　 Left := ALeft;<br>　 Top := ATop;<br>　 Width := AWidth;<br>　 Height := AHight;<br>end;<br>Result := AQRShapeV;<br>end;</font></p>
<p><font>procedure TForm1.Create_Title(Sender: TWinControl; ALeft, ATop, AWidth,<br>AHight: Integer; ACaption: String; AAlignMent: TAlignment);<br>var<br>AQRLabel : TQRLabel;<br>begin<br>AQRLabel := TQRLabel.Create(Nil);<br>with AQRLabel do<br>begin<br>　 Parent := Sender;<br>　 Left := ALeft;<br>　 Top := ATop;<br>　 Width := AWidth;<br>　 AlignMent := AAlignMent;<br>　 Caption := ACaption;<br>end;<br>end;<br>----------------------------- <br>2004年7月28日 16:00 </font></p>
<br><img src ="http://www.cppblog.com/WangYu/aggbug/42754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-15 08:58 <a href="http://www.cppblog.com/WangYu/articles/42754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>property</title><link>http://www.cppblog.com/WangYu/articles/42736.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Thu, 14 Feb 2008 09:08:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42736.html</guid><wfw:comment>http://www.cppblog.com/WangYu/comments/42736.html</wfw:comment><comments>http://www.cppblog.com/WangYu/articles/42736.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/WangYu/comments/commentRss/42736.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/WangYu/services/trackbacks/42736.html</trackback:ping><description><![CDATA[<h2>delphi的属性property和消息处理特点事件属性。</h2>
Delphi&nbsp;&nbsp;中的属性property（适合初学delphi）<br>
Propery In Delphi<br>
<br>
前言：<br>
适合delphi初学者，有面向对象知识和java或者vc编程经验人士阅读。<br>
一普通属性<br>
我们在delphi的类中常常能看到这样的代码：propert property 属性名 类型名 read 字符串1 write 字符串2<br>
这里属性的名字可能不同。都是这样的格式：property 属性名 read 字符串1 write 字符串2<br>
我以property Left: Integer read FLeft write
SetLeft;为例子，它是Tcontrol的属性，你可以在controls文件中找到。Left是一个Integer类型的属性。Read申明了访
问该变量要访问的变量或者方法，write申明了修改该变量时访问的变量或者方法。注意：可以是变量，也可以是方法，我在后面告诉大家这是怎么回事。这里
它是一个变量，名字叫做FLeft。出于封装的目的，我们一般都会把这样的变量放在private中间去，果然，在private中我们可以找到<br>
FLeft:
Integer这段代码（出于命名的习惯，我们把这样的变量取名为属性名前面加一个大写的F）。这样当你read该属性时，实际上你访问的是Fleft的
值。所以你可以写些方法来修改fleft，间接修改了left的值。然后我们再看SetLeft，这里它是一个方法（问我怎么知道？还是看命名规则，通常
用属性名前面加上Set），通常也会放在private中去，我们来验证一下，我们在private中看到申明：<br>
procedure SetLeft(Value: Integer);<br>
和如下代码实现：<br>
procedure TControl.SetLeft(Value: Integer);<br>
begin<br>
&nbsp;&nbsp;SetBounds(Value, FTop, FWidth, FHeight);<br>
&nbsp;&nbsp;Include(FScalingFlags, sfLeft);<br>
end;<br>
如果你写了如下代码改变left：control1.left:=23,那么程序调用了函数SetLeft(23),SetBounds是改变区域的函
数，这里你就明白了它封装了的好处，每次你改变left时它就会根据新的left而改变区域的大小，这个函数同时也改变了Fleft的大小，请查阅
SetBounds的源代码。<br>
procedure TControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);<br>
begin<br>
&nbsp;&nbsp;if CheckNewSize(AWidth, AHeight) and<br>
&nbsp; &nbsp; ((ALeft &lt;&gt; FLeft) or (ATop &lt;&gt; FTop) or<br>
&nbsp; &nbsp; (AWidth &lt;&gt; FWidth) or (AHeight &lt;&gt; FHeight)) then<br>
&nbsp;&nbsp;begin<br>
&nbsp; &nbsp; InvalidateControl(Visible, False);<br>
&nbsp; &nbsp;<strong>  FLeft := ALeft;</strong> <br>
&nbsp; &nbsp; FTop := ATop;<br>
&nbsp; &nbsp; FWidth := AWidth;<br>
&nbsp; &nbsp; FHeight := AHeight;<br>
&nbsp; &nbsp; UpdateAnchorRules;<br>
&nbsp; &nbsp; Invalidate;<br>
&nbsp; &nbsp; Perform(WM_WINDOWPOSCHANGED, 0, 0);<br>
&nbsp; &nbsp; RequestAlign;<br>
&nbsp; &nbsp; if not (csLoading in ComponentState) then Resize;<br>
&nbsp;&nbsp;end;<br>
end;<br>
这样外部就看起来只是通过赋值运算来改变了该属性的值。Read和write可以是变量，或者是函数，取决于你的设计。你当然可以这样写:
propert property 属性名 类型名 read 变量1 write 变量2。变量1和变量2可以是相同的。你也可以这样propert
property 属性名 类型名 read 方法1 write 方法2。任你组合。但是有2点要注意：<br>
1.&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;命名规则最好按习惯来，易于阅读。<br>
2.&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;如果是变量，那么类型要和属性的类型一致，如果是方法，那么入口参数要和属性的类型一致。<br>
二事件属性Tevent<br>
我们常常使用组件的事件属性，比方说click事件，可是我们很难从表面看出它是如何调用的呢，如何触发的呢。下面我来给你解答。<br>
我们在属性管理器object
inspector中看到event页onclick右边对应了一个方法的名字。我们其实可以这样给一个组件的事件对应上一个出来方法。以一个form为
例子Form1. OnMouseDown:=&#8216;你的方法&#8216;。注意方法的入口参数有讲究，这里是（Sender：TObject）<br>
我们还是一tcontrol为例子，我们找到这段代码：<br>
property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;跟上面讲的类似，不过这里有个特殊的类型，TNOtifyEvent，是个事件类型，我们找到它的申明：<br>
TMouseEvent = procedure(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer) of object;<br>
可以看到，它其实就是个函数，但是蓝色部分把入口参数限定了。那么我们通过赋值Form1.
OnMouseDown:=&#8216;你的方法&#8216;，就对应了OnMouseDown的方法。然后我们只要写了一段拦截鼠标消息的函数，在里面直接或间接调用
FonMouseDown，那么就把消息和处理函数对应上去了。这里它间接调用的层数比较多，讲起来比较费时间，涉及到Message类型，建议大家去看
下李维的书。<br>
以下附上间接调用过程，其实还要很多消息发生时也间接调用了，就不一一举出来了：（<br>
<br>
procedure WMRButtonDblClk(var Message: TWMRButtonDblClk); message WM_RBUTTONDBLCLK;//拦截消息的函数<br>
procedure TControl.WMRButtonDblClk(var Message: TWMRButtonDblClk);<br>
begin<br>
&nbsp;&nbsp;inherited;<br>
&nbsp;&nbsp;DoMouseDown(Message, mbRight, [ssDouble]);<br>
end;<br>
<br>
procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;<br>
Shift: TShiftState);<br>
procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton;<br>
&nbsp;&nbsp;Shift: TShiftState);<br>
begin<br>
&nbsp;&nbsp;if not (csNoStdEvents in ControlStyle) then<br>
&nbsp; &nbsp; with Message do<br>
&nbsp; &nbsp;&nbsp; &nbsp;if (Width &gt; 32768) or (Height &gt; 32768) then<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;with CalcCursorPos do<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; MouseDown(Button, KeysToShiftState(Keys) + Shift, X, Y)<br>
&nbsp; &nbsp;&nbsp; &nbsp;else<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;MouseDown(Button, KeysToShiftState(Keys) + Shift, Message.XPos, Message.YPos);<br>
end;<br>
<br>
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;<br>
X, Y: Integer); dynamic;<br>
procedure TControl.MouseDown(Button: TMouseButton;<br>
&nbsp;&nbsp;Shift: TShiftState; X, Y: Integer);<br>
begin<br>
&nbsp;&nbsp;if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y);<br>
end;<br>
<br>
好处：<br>
如果你多写自己的类，你会发现这样做是多么的方便，而不会像java要写getleft，setleft，然后把text放在private中，访问和修
改时要调用不同的方法，而delphi你都只是调用contol1.text来访问，control1.text:=&#8217;某字符串&#8217;来修改它的值。<br>
而在处理消息方面，基类把onclick，onmousedown这样的属性申明为protected，如果你要使用，可以申明为published就可
以出现在object
inspector里面，然后方便的写处理方法，你也可以不公开，而在ctreate函数中给它赋值，而不用像java那样，写listener那么复
杂。<br>
我的研究也不深，有什么不妥请指正：）。欢迎来信<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#119;&#101;&#110;&#106;&#117;&#110;&#119;&#117;&#52;&#51;&#48;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;">wenjunwu430@163.com</a><br>
<br><img src ="http://www.cppblog.com/WangYu/aggbug/42736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-14 17:08 <a href="http://www.cppblog.com/WangYu/articles/42736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TreeView使用笔记</title><link>http://www.cppblog.com/WangYu/articles/42726.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Thu, 14 Feb 2008 05:23:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42726.html</guid><description><![CDATA[<p>TreeView使用笔记</p>
<p>TreeView由节点构成，建树通过对TreeView.items属性进行操作。Items是一个TTreeNodes对象，这是一个TTreeNode集。</p>
<p>一、针对TTreeNodes,也就是 TreeView.Items,有这些属性：<br>1、count，节点个数。<br>2、item[index] ,通过index得到节点。</p>
<p>二、针对TTreeNodes,也就是 TreeView.Items,常用的添加节点的操作有：<br>AddFirst添加第一个根节点。由此函数添加的节点总排在前面，除非后来又使用此函数添加了一个节点，则后添加的节点将排在前面。该函数返回新添加的节点。<br>AddChildFirst添加第一个子节点，要求有父节点作为其参数。返回新添加的节点。<br>AddChild添加一个子节点，要求有父节点作为其参数。返回新添加的节点。<br>Add添加一个兄弟节点，要求有兄弟节点作为其参数。返回新添加的节点。</p>
<p>三、针对TTreeNodes,也就是 TreeView.Items,常用的得到节点的操作有：<br>GetFirstNode() 得到根节点。<br>然后配合TTreeNode.GetNext(),就可以访问所有的节点。</p>
<p>四、建树举例：</p>
<p>var<br>root_node,cur_node:TTreeNode;<br>begin<br>root_node:=AddFirst(nil,'根节点1');<br>cur_node:=addChildfirst(root_node,nil,'根节点1_child1');<br>add(cur_node,'根节点1_child2');<br>root_node:=Add(nil,'根节点2');<br>AddChildFirst(root_node,''根节点2_child1'); <br>end;</p>
<p>五、事件触发：<br>当从一个节点跳到另一个节点，会触发TTreeView.OnChange事件。该事件中，将传递node,即当前被选中的节点。</p>
<p>当修改一个节点的text时，会触发TTreeView.onEdit事件。</p>
<p><br>六、将节点和节点所对应的数据联系起来<br>对于每个TTreeNode,有个Data属性，可以存放一个指针。我们可以利用这个域来存放与节点对应的自己的数据。<br>1.我们先定义一个数据结构，作为记录我们要记录的数据。如：<br>type <br>PMyData=^TMyData;<br>TMyData=Record<br>sFName:string;<br>sLName:String;<br>nIndex:integer;<br>end;</p>
<p>2.然后，创建数时，将节点和节点数据联系起来：<br>procedure TForm1.Button1Click(Sender: TObject);<br>var<br>myshuju: PMyData<br>cur_node:TTreeNode;<br>begin<br>New(MyRecPtr); //记住，一定要先分配内存。有几个节点，就要分配几次内存。<br>myshuju^.FName:=Edit1.Text;<br>Myshuju^.LName := Edit2.Text;<br>TreeViewIndex := StrToInt(Edit3.Text);<br>with TreeView1 do<br>begin<br>cur_node:=items.AddFirst(nil,'first');<br>cur_node.data:=myshuju;<br>end;<br>end;</p>
<p>3.当我们选中一个节点时，就可以使用我们的数据了。<br>procedure TForm1.TreeView1Change(Sender:TObject;Node:TTreeNode);<br>begin<br>if node.data&lt;&gt;nil then<br>self.label1.caption:=pmyData(node.data)^.Fname+pmyData(node.data)^.Lname<br>end;</p>
<p><br>七、一般使用流程：<br>1、添加全局变量：<br>b_first:boolean; //记录是否是第一次访问节点，因为此时数据还未准备好，而一旦访问节点就会触发OnChange事件，在此事件处理函数中也许会出错。<br>2、在FormCreate中，<br>a、设置b_first:=true;<br>b. 创建数并将节点与数据联系。<br>3、在FormShow中<br>设置b_first:=false;<br>4.在事件OnChange中处理节点被选中事件。<br>5.在Edit中处理节点被修改Text事件。<br>并调用OnChange.<br>6.在 TreeView.Destory中<br>释放Data 中指向的内存空间。</p>
<br><img src ="http://www.cppblog.com/WangYu/aggbug/42726.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-14 13:23 <a href="http://www.cppblog.com/WangYu/articles/42726.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>容易混淆的概念</title><link>http://www.cppblog.com/WangYu/articles/42596.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Thu, 07 Feb 2008 17:35:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42596.html</guid><description><![CDATA[<p><strong> 一、Owner和Parent的区别： <br></strong> <br>　　Parent属性是指构件的包容器，构件只能在此范围内显示和移动。举例子如下： <br>　　(1)在Form1的窗体上，放一个Panel1,并将Panel1拉大， <br>　　(2)在Panel1上放一Button1； <br>　　(3)在Form1上放一Button2。 <br>
现在如果移动Panel1,则Button1随着Panel1移动，这是因为Button1的Parent是Panel1。现在将Button2移到
Panel1上，再次移动Panel1,Button2并不跟着移动，这是因为Button2的Parent是Form1。除在窗体设计中，应注意构件的
Parent是谁外，在动态创建构件时，也应指出构件的Parent，如在上例中继续操作： <br>　　1)Procedure Tform1.Button2click(Sender:Tobjet); <br>　　2)Var <br>　　3) Button:Tbutton; <br>　　4) Begin <br>　　5) Button:Tbutton.cerate(self); <br>　　6) Button.parent=panel1; <br>　　7) Button.lleft=0; <br>　　8) Button.top=0; <br>　　9) Button.caption:='OK'; <br>　　10) End； <br>
当按Button2时，将在Panel1上创建一个Button，而如果把第6句改为Button.parent:=self；按Button2时，将
在Form1上创建一个Button了。如果将第6句删除，按Button2时，什么都不会发生，这是因为创建方法无法知道应在哪里显示构件。 <br>　　Owner属性是指构件的所有者，它负责构件的创建和释放。如在上例中，系统默认窗体上所有构件的所有者是窗体，而窗体的所有者是Application。顺便指出，create方法应带有表示构件所有者的参数，如在上例中，构件所有者是窗体，即self。 <br>　　Parent属性和Owner属性是运行阶段的属性，只能在运行阶段，通过代码设置。　　 <br><strong> </strong> </p>
<br>
<p><strong> 二、Self和Sender的区别： <br></strong> <br>　　在事件处理程序参数表中，至少含有一个参数Sender,它代表触发事件处理程序的构件，如在上例中，Sender就指Button2，有了Sender参数，可以使多个构件共用相同的事件处理程序，如下例： <br>　　Procedure Tform1.Buttonclick(Sender:Tobject); <br>　　Begin <br>　　If sender=button1 then <br>　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　Label1.caption:='看庭前花开花落 '<br>　　Else Label2.caption:='望天上云卷云舒' <br>　　End； <br>　　在此例中，Button1,Button2共用了Buttonclick事件处理程序。 <br>　　Self是指所编的程序范围是在哪一个类中，<a href="http://www.jcwcn.com/html/Delphi" target="_blank">Delphi</a>中
大都在窗体范围内编程，因此，self即指窗体，如果在编写一个类或是一个组件，则self指该类或组件。我们在函数或过程的声明中可看出self是代表
哪个组件，即self代表&#8216;.&#8217;号之前的组件，如在第一个例子中，self代表Tform1。另外应注意，self只能用在类方法中，而不能用在过程或函
数中，如下例用法是错的: <br>Function a1(B:Integer):Integer; <br>　　Begin <br>　　&#8230;&#8230; <br>　　Button:=tbutton.create(self)；&#8230;&#8230; <br>　　End；　　 <br><strong> </strong> </p>
<br>
<p><strong> 三、Clientheight和Height,Clientwidth和Width的区别： <br></strong> <br>
对于一般的构件而言，Height就是Clientheight,Width就是Clientwidth，而对于窗体而言，Height是包括标题条在
内的高度，而Clientheight是指窗体工作区的高度。同理，Clientwidth是指定窗体工作区的宽度。 <br>　　从上面陈述可知，理解Ower和Parent,Self和Sender，Clientheight和Height,Clientwidth和Width区别，对于<a href="http://www.jcwcn.com/html/Delphi" target="_blank">Delphi</a>中正确编程是重要的。</p>
<br> <img src ="http://www.cppblog.com/WangYu/aggbug/42596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-08 01:35 <a href="http://www.cppblog.com/WangYu/articles/42596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>...Removing All Doubt</title><link>http://www.cppblog.com/WangYu/articles/42593.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Thu, 07 Feb 2008 16:00:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42593.html</guid><description><![CDATA[<div id="header">
<p id="sideTitle">
ChuckJ's Blog
Please read my <a href="http://www.removingalldoubt.com/FormatPage.aspx?path=SiteConfig/disclaimer.format.html">
disclaimer</a>.
It is better to be silent and thought a fool, than to speak and remove all doubt." -- Silvan Engel
</p>
<h1 id="title">
<a href="http://www.removingalldoubt.com/" style="text-decoration: none;">...Removing All Doubt</a></h1>
<p id="byline">Chuck Jazdzewski</p>
</div>
<form name="PermaLink" method="post" action="PermaLink.aspx" id="PermaLink">
    <div>
    <input name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTExMjIxODcwMzBkZP9szDQu8lyrXoOgwfjQjTKx+AL7" type="hidden">
    </div>
    <div id="content">
    <div class="entry">
    <h3 class="entryTitle">Fatherly Advice To New Programmers</h3>
    <div class="entryBody">
    <p>It looks like none of my children will become programmers. Instead of letting
    my fatherly advice to my new programmer son or daughter go to waste, I am going
    to inflict it on you. If you are newly embarking on the journey that is becoming
    a programmer, here is advice your father would tell you if he were a programmer.
    These are things I had to learn the hard way. </p>
    <p><strong>Keep Learning: </strong>Read. Go to conferences. Subscribe to journals. Take
    classes. Whatever it takes for you to keep learning, make it a priority. Learn
    about every language you can find. Take time to learn about any new frameworks,
    algorithms, techniques, models, paradigms, you can. Each gives you one more
    tool in your tool chest. Each will help you more easily tackle your next
    programming problem. Find a mentor, someone much better than you, and learn all
    they can teach you. Never stop learning.</p>
    <p><strong>Learn To Communicate</strong>: I often joke that the most important skill you
    can learn as a programmer is how to draw a rectangle on a white-board.
    Communication is critical to the job of a programmer. Communicating with
    customers, clients, users, co-workers, bosses, vice presidents, CEO's,
    board-members, VC capitalists, all will become important at some point in your
    career. Learn how to speak in public. Learn how to write in English. Learn to
    effectively communicate in person. Learn how to persuade without shouting,
    getting angry, or getting flustered. Learn how to speak without jargon. Help
    people understand what you are doing. Learn to break things into simple,
    understandable pieces. Learn to communicate by analogy and symbolism. Learn to
    communicate.</p>
    <p><strong>Be Predictable:</strong> Learn how fast you can comfortably program. Wait to
    predict how long it will take you to complete a task until you understand it.
    Allow for the unexpected. Plan for vacations and time-off. Live with your
    predictions. I don't believe I know a problem well enough to predict how long it
    will take to complete until I can break that task down into sub-tasks that each
    take no longer than 3 days (often less than one day). Live by this rule,
    under-promise, over-deliver. It is better to deliver in 10 days what you
    promised in 15 than to deliver in 10 days what you promised in 5. People depend,
    schedule, and plan around your predictions. Make them the best you can and make
    sure you can comfortably do them or you will be asked to live up to your
    uncomfortable predictions. You will not be good at it at first; to compensate,
    verify your predictions with someone more experienced. Learn to get better. Be
    predictable; other depend on you.</p>
    <p><strong>Own Up To Your Mistakes:</strong> You will make mistakes. How you handle your
    mistakes is how you will be judged. Learn how to say "I was wrong." If you
    underestimated how long it will take you to do something, tell people as soon as
    it is clear to you. If you broke the build, fix it. If you created a bug, fix
    it. Don't deny the mistake, don't make excuses for the mistake, don't figure out
    how to hide the mistake, don't blame others for the mistake, do something about
    it. Take ownership of your mistake or you <em>will</em> repeat it.</p>
    <p><strong>Never Let Bad Code Off Your Desk:</strong> Your job as a programmer is to write
    code that works, never let code off your desk you are not sure meets that
    criteria. Not only does it reflect badly on you, it is much more expensive, and
    much harder, to find a problem once it leaves your desk than before. Learn to
    love unit tests. Learn to love code coverage. Learn to test your code better
    than people who are paid to test it. Be embarrassed about bugs that are found
    after you have checked-in. Be especially embarrassed when a customer finds the
    bug. Don't rely on others to find your bugs for you, find them and fix them
    yourself. Don't hope it will work. Test it. Don't assume it will work. Test it.
    Don't whatever. Just test it. If you haven't tested it, it doesn't work; of this
    you <em>can</em> be sure. But, even if you are diligent with testing, bugs will
    get by you. You will make mistakes but try your best not to.</p>
    <p><strong>Programming is Fun But Shipping is Your Job:</strong> Programming is fun. It is
    the joy of discovery. It is the joy of creation. It is the joy of accomplishment. It
    is the joy of learning. It is fun to see your handiwork displaying on the
    screen. It is fun to have your co-workers marvel at your code. It is fun to have
    people use your work. It is fun have your product lauded in public, used by
    neighbors, and discussed in the press. Programming should be fun and if it
    isn't, figure out what is making it not fun and fix it. However, shipping isn't
    fun. I often have said that shipping a product feels good, like when someone
    stops hitting you. Your job is completing the product, fixing the bugs, and
    shipping. If bugs need fixing, fix them. If documentation needs writing, write
    it. If code needs testing, test it. All of this is part of shipping. You don't
    get paid to program, you get paid to ship. Be good at your job.</p>
    <p>Remember these simple statements,</p>
    <ul>
        <li>Never stop learning.</li>
        <li>Communication is critical.</li>
        <li>Under promise, over deliver.</li>
        <li>"I was wrong."</li>
        <li>If it is not tested it doesn't work.</li>
        <li>Programming isn't your job, shipping is.</li>
    </ul>
    </div>
    <p><a class="permalink" href="http://www.removingalldoubt.com/PermaLink.aspx/a32977e2-cb7d-42ea-9d25-5e539423affd">09/19/2006 9:43 AM</a> | <a href="http://www.removingalldoubt.com/commentview.aspx/a32977e2-cb7d-42ea-9d25-5e539423affd">Comments [32]</a> |  #<a href="http://www.removingalldoubt.com/categoryview.aspx/Programming">Programming</a></p>
    </div>
    </div>
</form>
<div id="footer">
<p id="copyright">Content &#169; 2007 Chuck Jazdzewski | Subscribe to my <a class="standardsButton" href="http://www.removingalldoubt.com/blogxbrowsing.asmx/GetRss?">RSS</a> feed.</p>
<p>All source code above is usable under the
<a href="http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx">
Microsoft Permissive License (Ms-PL)</a> unless otherwise specified in the
containing entry.</p>
<p id="poweredBy">Powered by <a href="http://www.simplegeek.com/">BlogX</a></p>
</div>
<!-- Generated by BlogX v.1.0.1.24 -->
<br> <img src ="http://www.cppblog.com/WangYu/aggbug/42593.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-08 00:00 <a href="http://www.cppblog.com/WangYu/articles/42593.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Delphi 7事件的多处理机制</title><link>http://www.cppblog.com/WangYu/articles/42581.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Wed, 06 Feb 2008 19:06:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42581.html</guid><description><![CDATA[<font face="Verdana" size="4">Delphi 7事件的多处理机制<br></font>Allen Tao<br>2007-08-19
<p>　　首先解释一下这个题目。在我使用Delphi
7的过程中发现，一个对象的事件只能被一个过程处理。如果多次给这个对象的事件赋给处理事件的过程，最后真正处理事件的将是最后赋值的那个过程。例如，有
类TMyClass中定义了一个事件OnSomeFired，在类TClientClass中该类被实例化，它的事件被处理。如下所示：<br>constructor TClientClass.Create;<br>var<br>&nbsp; myObj: TMyClass;<br>begin<br>&nbsp; //&#8230;<br>&nbsp; myObj:= TMyClass.Create;<br>&nbsp; myObj.OnSomeFired:= SomeFired1;<br>&nbsp; myObj.OnSomeFired:= SomeFired2;<br>　　这里的SomeFired1与SomeFired2都是TClientClass中定义的处理过程。其最终的结果是当OnSomeFired事件发生时，只有SomeFired2被调用。<br>　　但在编程的实际中，往往需要一个事件被多个方法所处理。为此，我参考了一些对这个问题的解决办法，总结得出了一个自己的方法，称为事件的多处理机制。
</p>
<p><font size="4"><strong> 原理</strong> </font><br>　　Delphi 7中的事件本质上是一种过程指针。但事件类型在定义时要比一般过程指针在最后多一个&#8220;of object&#8221;，如常用的TNotifyEvent的定义是：<br>TNotifyEvent = procedure(Sender: TObject) of object;<br>
因此，给一个事件属性赋值，也就是给一个类的过程指针类型的成员变量赋值，当然是最后一次的赋值才有效。要想多次赋值有效就必须有一个数据结构把每次赋
值赋给的过程指针变量都记录下来，最合适的数据结构当然是列表TList。但如果在每一个有事件的类中都加一个记录事件赋值的列表对象，自然是不方便的，
而且使用这个列表的代码在不同类中也差不多，应该抽取出来形成一个类。这个类就是事件多处理机制的核心内容。
</p>
<p><font size="4"><strong> 做法</strong> </font><br>　　要记录事件处理过程，就需要将过程指
针变量放入列表对象中。但列表对象只能添加指针类型对象（也就是引用类型），而过程指针变量是指类型变量，不能直接添加。这就需要有一个类对过程指针变量
进行包装，转化为指针类型对象。于是，先要定义包装类TCallMethod，如下所示：<br>TCallMethod = class<br>&nbsp; private<br>&nbsp;&nbsp;&nbsp; _callback: TMethod;<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; property Callback: TMethod read _callback write _callback;<br>&nbsp; end;<br>　　这里的TMethod是Delphi预定义的记录类型，任何过程指针变量都可以强制转化为这种类型。之后，再定义记录处理过程的类，如下所示：<br>&nbsp; TEventObject = class<br>&nbsp; private<br>&nbsp;&nbsp;&nbsp; callList: TList;<br>&nbsp;&nbsp;&nbsp; function GetItem(i: Integer): TMethod;<br>&nbsp;&nbsp;&nbsp; function GetCount: Integer;<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; constructor Create;
</p>
<p>&nbsp;&nbsp;&nbsp; procedure Add(p: TMethod);<br>&nbsp;&nbsp;&nbsp; procedure Remove(p: TMethod);
</p>
<p>&nbsp;&nbsp;&nbsp; property Count: Integer read GetCount;<br>&nbsp;&nbsp;&nbsp; property Items[i: Integer]: TMethod read GetItem;&nbsp; default;<br>&nbsp; end;<br>　　下面是实现部分：<br>constructor TEventObject.Create;<br>begin<br>&nbsp; callList:= TList.Create;<br>end;
</p>
<p>procedure TEventObject.Add(p: TMethod);<br>var<br>&nbsp; a: TCallMethod;<br>begin<br>&nbsp; a:= TCallMethod.Create;<br>&nbsp; a.Callback:= p;<br>&nbsp; callList.Add(a);<br>end;
</p>
<p>procedure TEventObject.Remove(p: TMethod);<br>var<br>&nbsp; i: Integer;<br>begin<br>&nbsp; for i:= 0 to GetCount - 1 do<br>&nbsp;&nbsp;&nbsp; if (TCallMethod(callList[i]).Callback.Code = p.Code) and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (TCallMethod(callList[i]).Callback.Data = p.Data) then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callList.Delete(i);<br>end;
</p>
<p>function TEventObject.GetCount: Integer;<br>begin<br>&nbsp; Result:= callList.Count;<br>end;
</p>
<p>function TEventObject.GetItem(i: Integer): TMethod;<br>var<br>&nbsp; a: TCallMethod;<br>begin<br>&nbsp; a:= TCallMethod(callList[i]);<br>&nbsp; Result:= a.Callback;<br>end;<br>　　从上面的代码可以看到，TEventObject的目的是包装TList对象，屏蔽了TList类的大多数方法，只对外暴露了2个过程、1个属性与1个索引，分别用于添加、删除处理事件过程、获得过程个数及通过序号检索记录的过程变量。
</p>
<p><font size="4"><strong> 使用<br></strong> </font>　　使用时，在需要使用事件的类中将事件的类型由过程指针改为TEventObject，即可使该事件拥有多处理的能力。如下面代码所示：<br>&nbsp; TMyClass=class<br>&nbsp; private<br>&nbsp;&nbsp;&nbsp; someFired: TEventObject;<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; constructor Create;<br>&nbsp;&nbsp;&nbsp; procedure DoSth;<br>&nbsp;&nbsp;&nbsp; property OnSomeFired: TEventObject read someFired write someFired; // 多处理事件<br>&nbsp; end;<br>　　以下是实现代码：<br>constructor TMyClass.Create;<br>begin<br>&nbsp; Self.someFired:= TEventObject.Create;<br>end;
</p>
<p>procedure TMyClass.DoSth;<br>var<br>&nbsp; i: Integer;<br>&nbsp; method: TNotifyEvent;<br>begin<br>&nbsp; if someFired.Count &gt; 0 then<br>&nbsp;&nbsp;&nbsp; for i:= 0&nbsp; to someFired.Count - 1 do<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method:= TNotifyEvent(someFired[i]);&nbsp;// 在该类中事件的真正类型是TNotifyEvent，因此在触发事件时先要转成这种类型的过程指针后再进行调用<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(Self);<br>&nbsp;&nbsp;&nbsp; end;<br>end;<br>　　定义了一个包含多处理事件的类后，再看看这种类如果在其客户类中被调用。如以下代码：<br>{类TClientClass中}<br>var<br>&nbsp; myObj: TMyClass;<br>&nbsp; //&#8230;<br>&nbsp; myObj:= TMyClass.Create;<br>&nbsp; myObj.OnSomeFired.Add(SomeFired1);<br>&nbsp; myObj.OnSomeFired.Add(SomeFired2);<br>　　当客户类代码中调用TMyClass的DoSth方法时，事件将被触发，而2个处理过程SomeFired1与SomeFired2则会依次调用，实现多处理的目的。
</p>
<p><font size="4"><strong> 再发展一步</strong> </font><br>　　上面的TEventObject
类可以实现事件多处理的目的，但它对加入其中的过程指针类型没有检查，这是一个隐患。因此可以针对每一种事件要求的过程指针类型从
TEventObject继承一个类，实现类型检查。如要求事件的类型是TNotifyEvent，就可以继承一个TNotifyEventObject
类，如下面代码：<br>&nbsp; TNotifyEventObject = class(TEventObject)<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; procedure Add(p: TNotifyEvent); overload;<br>&nbsp;&nbsp;&nbsp; procedure Remove(p: TNotifyEvent); overload;<br>&nbsp; end;<br>　　以下是实现部分：<br>procedure TNotifyEventObject.Add(p: TNotifyEvent);<br>begin<br>&nbsp; inherited Add(TMethod(p));<br>end;
</p>
<p>procedure TNotifyEventObject.Remove(p: TNotifyEvent);<br>begin<br>&nbsp; inherited Remove(TMethod(p));<br>end;<br>　　在这个类中重载了添加与移除方法，可以有效地对过程指针类型进行检查。
</p>
<br><img src ="http://www.cppblog.com/WangYu/aggbug/42581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-07 03:06 <a href="http://www.cppblog.com/WangYu/articles/42581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Action高级开发</title><link>http://www.cppblog.com/WangYu/articles/42559.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Tue, 05 Feb 2008 18:45:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42559.html</guid><description><![CDATA[<div class="postText">
<h2 align="center">Action高级开发</h2>
<p>Action开发篇</p>
<p>&nbsp;&nbsp;&nbsp;
在讨论Action的开发前，我想先讨论一下为什么要使用TActionList及TAction。从Delphi
4开始Borland提供了TActionList控件，ActionList提供了一种全新的设计用户界面交互模式的方法。传统的事件模式无法解决命令
状态更新的问题，因为任何情况下命令都是有效的。Delphi
4通过使用ActionList及Action提供了新的方法来处理命令的实现，即命令的有效性问题。ActionList是一个非可视的控件里面包含了
一组TAction对象。两者的关系有点像菜单项同菜单的关系。</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image001.jpg" height="205" width="280"></p>
            <p>图3.18</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp;
一个TAction对象提供一个命令，比如删除一个目标的选项（例如删除列表框的一个列表项），当Action控制的控件相应某些用户的输入会激发相应的
Action命令，通常是鼠标键盘点击等动作。Action通常用来控制按钮和菜单项这类控件，通过设定这类控件的Action属性可以把两者关联起来。</p>
<p>&nbsp;&nbsp;&nbsp; 图3.18显示了一个关联Action和控件的例子，EditCut1
Action被指定给SpeedButton1的Action属性。当关联完成后，Speedbutton的属性会根据对应的EditCut1的属性作出
相应的变化，比如按钮的Caption会自动变成&#8220;Cu&amp;t.&#8221;，当用户点击Cut按钮时，由EditCut1
Action实现的相应命令就会被调用。当Memo1中有文本被选中的时候，Cut按钮才是有效的，这是因为Delphi内置的EditCut
Action实现了Action的OnUpdate事件，在那里对相关联的Memo1进行了判断，只有当Memo1中有文本被选择了，Action对应剪
切操作才有效。</p>
<p>&nbsp;&nbsp;&nbsp;
参看前面在OTA部分实现的Winamp专家中，大量使用TAction对系统进行了管理，可以发现只需要在Action的OnUpdate事件中写很少
的代码甚至不写代码（对系统内置的Action而言），就可以对一些基本界面操作的有效性进行判断，同时使用ActionList容器类使得命令更容易维
护。另外，除了以上功能，还可以利用Action的OnHint事件对关联控件的飞跃提示的显示进行控制。</p>
<p>&nbsp;&nbsp;&nbsp;
虽然Action的使用是非常方便的，但如果想得到最佳的性能和表现，我们还是需要更深入地了解Action的工作原理。比如注意到
TActionList和TAction都定义了OnExecute和OnUpdate事件。两者有什么区别呢？在Borland的文档中并没有很清楚的
说明，所以还是需要研究一下Action工作的内部机制。</p>
<p>&nbsp;&nbsp;&nbsp; 当在程序中使用了ActionList和Action后，Delphi
中的Application对象会在系统空闲的时候产生OnUpdate事件。对于每一个ActionList，
TActionList.OnUpdate事件会最先生成，然后系统传递给事件两个参数。Action参数代表正在更新状态的Action，
Handled参数用来控制相应的Action的OnUpdate事件是否被调用。如果不想相应的Action的Update事件被调用，需要设定
Handled参数为真。</p>
<p>&nbsp;&nbsp;&nbsp;
TActionList.OnUpdate事件对每一个列表中的Action相关联的每一个控件都要产生一遍。换句话，假设ActionList1包含
Action1和Action2。现在假定Button1和SpeedButton1的 Action
属性设定为Action1，同时SpeedButton2的Action属性设定为Action2。在这种情况下，每个循环下来，
ActionList1.OnUpdate事件将会产生1+2＝3次。</p>
<p>&nbsp;&nbsp;&nbsp;
什么时候用Action.OnUpdate事件，什么时候用ActionList.OnUpdate事件没有什么绝对的准则。但一般来说，
TActionList.OnUpdate事件更容易控制，因为把全部的状态控制代码写在一个地方更清楚，而且写起来更简洁，当然这只是我的看法。</p>
<p>&nbsp;&nbsp;&nbsp;
另外，大家可能会注意到可以在一个窗体上放多个ActionList。比如Delphi带的RichEdit的演示程序中就使用了两个
ActionList，为什么使用两个ActionList呢?其实这是出于运行效率的考虑，因为按照ActionList更新状态的方式，如果你有一组
Action并不需要进行有效性校验。这时就应该把它们放到单独的ActionLis中去，这样就不需要生成OnUpdate事件了。这是因为不管
Action是否生成了OnUpdate事件，只要ActionList中有一个Action定义了OnUpdate事件，其他Action的
OnUpdate都会被调用。</p>
<p>&nbsp;&nbsp;&nbsp; 同时由于OnUpdate事件会产生很多次，所以不要在OnUpdate事件处理函数中写耗时很长的代码，这样会严重影响应用程序的运行效率。</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image002.gif" height="199" width="212"></p>
            <p>图3.19</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp;
同OnUpdate事件相反，我推荐为每一个Action对象生成一个OnExecute的事件，而不是全部写到
TActionList.OnExecute事件中去，它不同于OnUpdate事件之处在于只有当相应操作需要执行时，事件才会被调用，而
OnUpdate事件是只要系统空闲就会被调用。</p>
<p>&nbsp;&nbsp;&nbsp;
在Delphi的在线帮助中，关于Action执行过程中事件产生的顺序有点模糊不清。图3.19显示了一个更加清晰的顺序图。注意Action的
OnExecute事件发生在ActionList和Application 对象获得一个机会去处理Action之后。</p>
<p>&nbsp;&nbsp;&nbsp;
除了OnUpdate和OnExecute事件外，TActionList还定义了OnChange事件，这是一个非常奇怪的事件，只有当Action的
Category属性改变的时候或者是当ActionList的Images属性改变的时候才会被调用，我不清楚有什么必要生成这么样一个事件，因为
Category只是在设计时才有效，Images也极少在运行时改变，所以我觉得这个事件定义的好像没有必要。相比之下，OnHint事件更有用些，生
成一个OnHint事件处理过程使我们可以比较容易定制要显示的飞跃提示。OnHint事件有两个参数：第一个是HintStr，用它来返回要显示的提示
字符串；第二个参数CanShow用来确定是否显示提示。这个事件处理有点问题，在无焦点的控件如SpeedButton定义的OnHint事件中，
CanShow参数只在Action的Hint属性设置为空字符串的情况下才有效，如果Hint属性指定了一个字符串，那么不管CanShow如何设置，
控件只会显示缺省的Hint字符串。</p>
<p>&nbsp;&nbsp;&nbsp;
Action还具有给菜单项或SpeedButton添加图标的功能。但要注意仅仅设定TActiongList.Images属性是不够的，还需要同时
设定Menu的Images属性为相同的图像列表。另外假设我们要修改同Action关联的图像，仅仅修改对应的图像列表是不够的，不像Action的其
他属性（如：Caption或ShortCut），只要修改了，就会通知相应控件作出改变。我们必须先清除对应控件的Action属性，在重置
Action属性达到刷新控件的Glyph属性的目的。</p>
<p>&nbsp;&nbsp;&nbsp; 如果不想让Action的图像出现在对应的SpeedButton上时，我们必须在运行时（如在窗体的OnCreate事件中）清除SpeedButton的Glyph属性，在设计时清空Glyph属性是无效的。</p>
<p>&nbsp;&nbsp;&nbsp; 下一节将探讨如何重用现有Action源代码来实现新的Action类。 </p>
<p>设计新武器 ——Action的开发</p>
<p>&nbsp;&nbsp;&nbsp; 这节将探讨如何编写新的Action。</p>
<p>&nbsp;&nbsp;&nbsp; 首先，必须清楚Action是控件，我们可以像写其它VCL控件一样，编写并能注册它。</p>
<p>&nbsp;&nbsp;&nbsp;
其次，编写新的Action是有一定前提条件的，要编写的Action必须是对应于比较普遍的操作，可重用性要强，比如从一个列表框中删除列表项的操作以
及上下移动列表项都是在界面交互时会经常碰到的需求。当然通过在普通的TAction的OnExecute和OnUpdate事件中也可以实现这类操作，
但如果能通过编写对应于这样操作的Action，就可以省去重复编写OnUpdate和OnExecute事件处理过程的工作。</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image003.jpg" height="266" width="183"></p>
            <p>图3.20</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp;
另外要弄清，要编写的Action不同于一般意义上的Action的，我们定制的Action是用户可以不需要写OnExecute事件处理过程的，它提
供了一个内置的缺省功能，就好像TEditCutAction一样，只要把它同编辑框相关联，无需写一行代码它就可以正确处理剪切操作了。同样对于
OnUpdate事件，用户定制的Action也提供了内置的命令有效性校验机制，用户可以无需修改直接使用。但最重要的区别恐怕是用户定制的
Action可以用于很多不同的控件并能在不同程序中重用。</p>
<p>1. 预定义的Action</p>
<p>&nbsp;&nbsp;&nbsp;
在开始编写定制的Action前，先来看看Delphi已经实现了的定制的Action。图3.20中列出了Delphi自带的标准Action，
Edit Action处理剪贴板操作，Window Action处理多文档界面（MDI）的子窗体的管理操作，DataSet
Action则处理数据库导航命令。</p>
<p>&nbsp;&nbsp;&nbsp;
预定义的Action提供了很强大的功能。比如假定把一个SpeedButton的Action属性同一个TEditCut
Action相关联，当任何编辑框中的文本被选择后，SpeedButton就处于有效状态，这时点击SpeedButton，被选的文本将被删除并复制
到剪贴板上。所有这些功能不需要写任何代码。</p>
<p>&nbsp;&nbsp;&nbsp;
虽然预定义的Action功能很强大，但还是有一些应用上的限制。为了这些限制存在的原因，我们需要了解定制的Action是如何定位操作目标控件的。比
如当一个TEditCut Action 被激发的时候，它是如何知道操作是在Memo1上而不是在Edit2上的，也就是如何区分需要剪切的控件的？</p>
<p>2. 定位目标</p>
<p>&nbsp;&nbsp;&nbsp; 回忆一下前面讲过的，当一个Action被激发时，有4种可能的响应会发生：第一，Action
List在它的OnExecute事件中处理Action；第二，Application对象会处理Action；第三，Action调用本身的
OnExecute事件处理过程；如果这时Action还没有被处理，一个cm_ActionExecute 消息被发送到Application对象。</p>
<p>&nbsp;&nbsp;&nbsp; 当Application对象接收到这个消息，它首先把消息发到Screen对象管理的当前激活的窗体，如果当前没有活动的窗体，消息就发给应用程序的主窗体。</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image004.jpg" height="264" width="231"></p>
            <p>图3.21</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp; 消息由TCustomForm.CM-ActionExecute
消息处理过程进行处理。首先，处理过程检查窗体的ActiveControl属性。如果不为nil，当前活动控件的ExecuteAction方法就会被
调用，如果ActiveControl属性为nil，窗体的ExecuteAction方法会被调用(见图3.21)。</p>
<p>&nbsp;&nbsp;&nbsp;
ExecuteAction是一个布尔函数，定义在TComponent类中。如果控件对Action做出了响应，ExecuteAction
函数就返回真值。ExecuteAction函数会调用Action的HandlesTarget方法，并把它的引用参考传给控件。
HandlesTarget方法决定控件是否是Action的一个有效的操作对象。比如TEditAction对象HandlesTarget只有当目标
控件是从TCustomEdit继承下来的时候才返回真值。</p>
<p>&nbsp;&nbsp;&nbsp;
如果控件是一个有效的操作对象，Action的ExecuteTarget方法将被调用，同HandlesTarget类似，它接收一个目标控件的引用参
考作为一个参数，ExecuteTarget方法对目标控件执行相应的Action。图3.21举例说明了这一处理流程。</p>
<p>&nbsp;&nbsp;&nbsp; 如果ActiveControl和窗体都不是一个有效的操作目标，这种情况下，窗体的CM-ActionExecute方法会遍历窗体上的全部控件并对每一个控件都调用ExecuteAction方法直到一个有效的目标控件被找到或是找不到满足要求的控件为止。</p>
<br style="page-break-before: always;" clear="all">
<p>3. 定制Action的局限性</p>
<p>&nbsp;&nbsp;&nbsp;
理解了定制的Action如何定位它的目标控件，就可以讨论它们的局限性了。第一个局限来自于对ActiveControl的依赖性。因为当激发
Action的控件改变了输入焦点的时候，定制的Action可能无法正常工作，这对于SpeedButton和菜单项是没有影响的，因为它们没有输入焦
点，不会改变ActiveControl。然而普通的按钮却会改变输入焦点。</p>
<p>&nbsp;&nbsp;&nbsp;
假定Button1的Action属性设定为EditCut1。同时假定Edit1当前获得了焦点，并且其中的文本被选择了。当用户点击了
Button1，发生的第一件事是输入焦点切换到了按钮上。这时，
EditCut1.OnUpdate事件被自动调用。因为现在Button1成了ActiveControl，而它并不是TCustomEdit的子类，
EditCut1.Enabled的属性将设为False。结果Action变成无效的了，
Button1也同样失效了。当用户松开鼠标后，由于Button1无效了，结果OnClick事件就无法调用了，也就无法激发EditCut1的操作
了。</p>
<p>&nbsp;&nbsp;&nbsp;
这里还有一些其他限制，特别当这些编辑操作只能工作在TCustomEdit子类上时，它无法支持很多支持剪贴板操作的其他控件。比如，Edit
Action不能工作在csDropDown样式的组合列表框，而且在设计时，无法控制Edit
Action只对某个编辑框起作用。它只对所有的控件起作用（不过在运行时倒是可以通过TeditAction的Control属性进行控制）。</p>
<p>&nbsp;&nbsp;&nbsp; 指出这些限制并不会影响这些Action的重要性，这只是为了理解整个Action的工作流程。</p>
<p>4. 创建用户定制的Action</p>
<p>&nbsp;&nbsp;&nbsp; 创建Action同创建VCL控件非常类似，其实这并不奇怪，因为Action实际上就是控件的一种。编写控件的规则同样适用于Action。不过编写Action之前，先看一下Action类的继承关系。</p>
<p>&nbsp;&nbsp;&nbsp;
图3.22是非数据库Action的继承关系。编写新的Action，通常是从TAction开始的，但也应该了解一下全部的4个Action基类之间的
关系： TBasicAction、TContainedAction、TcustomAction和TAction。</p>
<p>&nbsp;&nbsp;&nbsp; TBasicAction是最低层的类，如果想要创建一个同非菜单或控件相关的Action，可以由它开始继承。</p>
<p>&nbsp;&nbsp;&nbsp; TContainedAction是直接从TbasicAction继承的类，增加了分类的功能，支持TActionList中的分类(Category)。</p>
<p>&nbsp;&nbsp;&nbsp; TCustomAction直接从TContainedAction继承下来，增加了针对菜单项和控件的功能， TcustomAction没有公开它的属性，仅仅是实现了它们。</p>
<p>&nbsp;&nbsp;&nbsp; TAction仅仅是公开了TcustomAction实现了的属性。它没有引进新的功能，除非需要使特定的属性隐藏，通常Action都是从TAction继承的。</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://hubdog.csdn.net/Hubdog/TAction.files/image005.jpg" height="216" width="363"></p>
<p>图3.22</p>
<p>5. 定制Action</p>
<p>&nbsp;&nbsp;&nbsp; 现在就可以开始编写Action了，创建一个定制的Action包括以下几个步骤：</p>
<p>&nbsp;&nbsp;&nbsp; (1) 需要创建一个新单元，在单元中要从一个基类（比如TAction）继承我们的Action。</p>
<p>&nbsp;&nbsp;&nbsp; (2) 然后，需要重载一些关键的方法像HandlesTarget、UpdateTarget和ExecuteTarget方法。</p>
<p>&nbsp;&nbsp;&nbsp; (3) 最后注册新的Action把它安装到Delphi中去。</p>
<p>&nbsp;&nbsp;&nbsp;
在线帮助建议使用StdActns单元作为创建定制的Action的一个指导。但是如果按照StdActns单元来写的话，生成的Action同
Delphi预定义的Action工作的方式无法完全一样。比如，用Action List Editor创建一个TEditPaste
Action，然后选择这个Action，对象编辑器显示的Caption已经初始化为&#8220;&amp;Paste&#8221;，提示初始化为&#8220;Paste&#8221;、
ImageIndex为2以及ShortCut为Ctrl+V。而生成的Action却无法做到对缺省属性值的初始化。</p>
<p>&nbsp;&nbsp;&nbsp;
如果创建一个基于StdActns单元中代码的Action，唯一能够初始化的属性是Caption，而且这个Caption被初始化为同Name一样的
无意义的值，像&#8220;MyAction1&#8221;这样无聊的名字。造成这种情况的根本原因将在后面的Action的安装部分讲到，这里通过提供一个
Constructor来初始化缺省的属性值。</p>
<p>&nbsp;&nbsp;&nbsp; 另外，StdActns单元比较糟糕的地方是在这些单元中并没有如何注册和安装Action的内容。Action虽然是一种控件，但它的注册同普通控件是不同的。更离谱的是在线帮助中关于如何注册Action是错误的。</p>
<p>6. 列表框Action</p>
<p>&nbsp;&nbsp;&nbsp; 虽然StdActns单元不是一个很好的指导文件，但只能从它开始研究。下面将创建一组Action用来提供对列表框的操作。后面的程序清单1显示了实现这组Action的源代码。</p>
<p>&nbsp;&nbsp;&nbsp; 类似标准的Edit
Action，从一个TListBoxAction基类开始处理目标控件的确定过程。这个类重载了HandlesTarget和UpdateTarget
方法。如果目标控件是TCustomListBox的子类，HandlesTarget方法将返回真值，如果列表框不是空的，有列表项存在，
UpdateTarget方法设定Action的Enabled属性为真。相应代码如下：</p>
<p>&nbsp;&nbsp;&nbsp; function TListBoxAction.HandlesTarget( Target: TObject ): Boolean;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //当Target为TCustomListbox的子类时，相应的命令才有效 </p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Result := ( ( Control &lt;&gt; nil ) and ( Target = Control ) or</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;( Control = nil ) and ( Target is TCustomListBox ) ) and</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TCustomListBox( Target ).Focused;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxAction.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //当相应的列表框不为空，包含列表项时，命令才有效</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := GetControl( Target ).Items.Count &gt; 0;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; TlistBoxAction剩下的部分用来支持Control属性。
TeditAction同样实现了一个Control
属性，可以使Action只对某一控件起作用，不过它的缺点是它声明为public属性，这样只能在运行时才能对它进行设置。这里声明Control为
published属性，这样在设计时就可以方便地进行设置了。不设定Control属性的话，Action将作用于窗体上全部的列表框。</p>
<p>&nbsp;&nbsp;&nbsp;
所有的派生类在结构上都比较类似，他们从同一个基类继承并且都重载了Constructor来初始化自身属性，还都重载了ExecuteTarget方法
来实现内置的执行功能。下面是TListBoxDelete的ExecuteTarget方法的实现，用来删除当前被选的列表项：</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxDelete.ExecuteTarget(Target: TObject);</p>
<p>&nbsp;&nbsp;&nbsp; var</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx: Integer;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx := GetControl( Target ).ItemIndex;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if Idx &lt;&gt; -1 then</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;GetControl( Target ).Items.Delete( Idx );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp;
一部分子类还重载了UpdateTarget方法，因为确定命令是否有效的规则对不同操作是不同的，同在TListBoxAction中实现的有可能不
同。例如，TListBoxMoveUp.UpdateTarget方法
判断Enabled属性是否为真是以被选的列表项是否是列表中的第一项为依据的，如果为第一项则上移的操作是无效的。代码示意如下：</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxMoveUp.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := GetControl( Target ).ItemIndex &gt; 0;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>7. 注册和安装Action</p>
<p>&nbsp;&nbsp;&nbsp; 完成Action的定义和编码后，需要注册安装Action到Delphi。这里使用一个单独的单元来进行注册。清单2列出了注册单元代码。</p>
<p>&nbsp;&nbsp;&nbsp;
同一般控件不同，注册Action我们需要使用RegisterActions过程，而不是RegisterComponents过程。
RegisterActions过程需要三个参数：第一个是一个描述Action分类的字符串，由于Action是专门针对列表框的，所以设定这个参数为
&#8220;ListBox.&#8221;；第二个参数是一组要注册的Action的类；最后一个参数称为Resource参数，这个参数在在线帮助里没有提到，它的类型是
TComponentClass，稍后会详细研究这个参数的具体用意，这里先不管它，把它直接设成TlistBox就可以。</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image006.jpg" height="252" width="252"></p>
            <p>图3.23</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp; 因为Action实际上是一种控件，要安装的话，必须把实现的单元放到包中然后进行注册。一旦安装到了Delphi里，新定制的Action就会出现在标准Action对话框中，如图3.23所示。</p>
<p>&nbsp;&nbsp;&nbsp;
最后还剩下一点问题那就是因为TCustomAction.DoHint（提示事件分发方法）在Delphi 4中声明为静态方法，而在Delphi
5声明为动态方法。这样在Delphi 5中就可以对提示处理进行重载，提供一个用户定制的提示处理，而在Delphi
4这是办不到的。本文的例子没有实现提示的定制部分，这个问题留给读者去实现。</p>
<p>&nbsp;&nbsp;&nbsp; 程序清单1 &#8211; ListActn.pas如下：</p>
<p>&nbsp;&nbsp;&nbsp; unit ListActn;</p>
<p>&nbsp;&nbsp;&nbsp; interface</p>
<p>&nbsp;&nbsp;&nbsp; uses</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Classes, ActnList, StdCtrls;</p>
<p>&nbsp;&nbsp;&nbsp; type</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxAction = class( TAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;private</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;FControl: TCustomListBox;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure SetControl( Value: TCustomListBox );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;protected</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;function GetControl( Target: TObject ): TCustomListBox; virtual;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure Notification( AComponent: TComponent; Operation: TOperation ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;function HandlesTarget( Target: TObject ): Boolean; override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure UpdateTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;published</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;property Control: TCustomListBox</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;read FControl</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;write SetControl;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxDelete = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure UpdateTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxClear = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxMoveUp = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure UpdateTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxMoveDown = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure UpdateTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxSelectAll = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;TListBoxUnselectAll = class( TListBoxAction )</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;public</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;constructor Create( AOwner: TComponent ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;procedure ExecuteTarget( Target: TObject ); override;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; implementation</p>
<p>&nbsp;&nbsp;&nbsp; uses</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Windows, Messages;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {== TListBoxAction Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; function TListBoxAction.GetControl( Target: TObject ): TCustomListBox;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Result := Target as TCustomListBox;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; function TListBoxAction.HandlesTarget( Target: TObject ): Boolean;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Result := ( ( Control &lt;&gt; nil ) and ( Target = Control ) or</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;( Control = nil ) and ( Target is TCustomListBox ) ) and</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;TCustomListBox( Target ).Focused;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxAction.Notification( AComponent: TComponent; Operation: TOperation );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Notification( AComponent, Operation );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if ( Operation = opRemove ) and ( AComponent = Control ) then</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;Control := nil;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxAction.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := GetControl( Target ).Items.Count &gt; 0;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxAction.SetControl( Value: TCustomListBox );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if Value &lt;&gt; FControl then</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;FControl := Value;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if Value &lt;&gt; nil then</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Value.FreeNotification( Self );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;end;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxDelete Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxDelete.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Delete';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 1;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Delete Item';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxDelete.ExecuteTarget(Target: TObject);</p>
<p>&nbsp;&nbsp;&nbsp; var</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx: Integer;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx := GetControl( Target ).ItemIndex;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if Idx &lt;&gt; -1 then</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;GetControl( Target ).Items.Delete( Idx );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxDelete.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := ( GetControl( Target ).Items.Count &gt; 0 ) and </p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;( GetControl( Target ).ItemIndex &lt;&gt; -1 );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxClear Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxClear.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Clear';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 2;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Clear List';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxClear.ExecuteTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;GetControl( Target ).Clear;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxMoveUp Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxMoveUp.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Move Up';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 3;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Move Item Up';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxMoveUp.ExecuteTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; var</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx: Integer;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx := GetControl( Target ).ItemIndex;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;GetControl( Target ).Items.Exchange( Idx, Idx - 1 );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;GetControl( Target ).ItemIndex := Idx - 1;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxMoveUp.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := GetControl( Target ).ItemIndex &gt; 0;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxMoveDown Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxMoveDown.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Move Down';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 4;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Move Item Down';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxMoveDown.ExecuteTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; var</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx: Integer;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Idx := GetControl( Target ).ItemIndex;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;GetControl( Target ).Items.Exchange( Idx, Idx + 1 );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;GetControl( Target ).ItemIndex := Idx + 1;</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxMoveDown.UpdateTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; var</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;L: TCustomListBox;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;L := GetControl( Target );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Enabled := ( L.ItemIndex &lt;&gt; -1 ) and</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( L.ItemIndex &lt; L.Items.Count - 1 );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxSelectAll Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxSelectAll.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Select All';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 5;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Select All Items';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxSelectAll.ExecuteTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;SendMessage( GetControl( Target ).Handle, LB_SETSEL, 1, -1 );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; {== TListBoxUnselectAll Methods ==}</p>
<p>&nbsp;&nbsp;&nbsp; constructor TListBoxUnselectAll.Create( AOwner: TComponent );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;inherited Create( AOwner );</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Caption := 'Unselect All';</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;ImageIndex := 6;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Hint := 'Unselect All Items';</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; procedure TListBoxUnselectAll.ExecuteTarget( Target: TObject );</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;SendMessage( GetControl( Target ).Handle, LB_SETSEL, 0, -1 );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; end.</p>
<p>&nbsp;&nbsp;&nbsp; 程序清单2 - ListActnReg.pas如下：</p>
<p>&nbsp;&nbsp;&nbsp; unit listActnReg;</p>
<p>&nbsp;&nbsp;&nbsp; interface</p>
<p>&nbsp;&nbsp;&nbsp; procedure Register;</p>
<p>&nbsp;&nbsp;&nbsp; implementation</p>
<p>&nbsp;&nbsp;&nbsp; uses</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Classes, ActnList, StdCtrls,ListActn;</p>
<p>&nbsp;&nbsp;&nbsp; procedure Register;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;RegisterActions( 'ListBox', [ TListBoxDelete, TListBoxClear, TListBoxMoveUp, TListBoxMoveDown,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TListBoxSelectAll, TListBoxUnselectAll ], TListBox );</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp; end.</p>
<p>&nbsp;&nbsp;&nbsp; Resource 参数：</p>
<p>&nbsp;&nbsp;&nbsp; 上面留了一个问题，就是Resource参数到底是干什么用的？现在是该揭开谜底的时候了。</p>
<p>&nbsp;&nbsp;&nbsp; 其实Resource参数可以为我们的Action属性初始化，并且可以在Action中嵌入指定的图像。实际上就是起到了Constructor过程的作用，它还能给Action添加缺省图标。下面就是使用Resource参数的步骤：</p>
<p>&nbsp;&nbsp;&nbsp; （1）给原来的安装包添加一个资源窗体。</p>
<p>&nbsp;&nbsp;&nbsp; （2）重新编译和安装Action。</p>
<p>&nbsp;&nbsp;&nbsp;
第一步，添加一个新的窗体，把它命名为TListBoxRes。然后添加一个图像列表控件，一个ActionList并添加我们先前创建的Action。
最后，向ImageList中添加图标，并设定Action的各项缺省属性，包括图标、Capition等等，最后把它保存为ListRes.Pas。</p>
<p>&nbsp;&nbsp;&nbsp;
第二步，因为要使用Resource参数来重新注册，这回系统会根据资源窗体的内容设定Action的缺省属性。这时就不再需要Action中的
Constructor过程了（这回终于明白为什么Borland的StdActns单元中实现的Action都没有constructor过程），把这
些过程去掉。并把先前RegisterAction中Resource参数的Tlistbox改成TlistBoxRes，然后把TlistBoxRes
对应的单元添加到注册单元的Uses列表中，下面是修改后的注册部分代码：</p>
<p>&nbsp;&nbsp;&nbsp; uses</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;Classes, ActnList, StdCtrls,ListActn,ListRes;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;</p>
<p>&nbsp;&nbsp;&nbsp; procedure Register;</p>
<p>&nbsp;&nbsp;&nbsp; begin</p>
<div>
<table align="left" cellpadding="0" cellspacing="0" hspace="0" vspace="0">
    <tbody>
        <tr>
            <td style="padding: 0mm 9pt;" align="left" valign="top">
            <p><img src="http://hubdog.csdn.net/Hubdog/TAction.files/image007.jpg" height="157" width="197"></p>
            <p>图3.24</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;RegisterActions(
'ListBox', [ TListBoxDelete, TListBoxClear, TListBoxMoveUp,
TListBoxMoveDown, TListBoxSelectAll, TListBoxUnselectAll ], TListBoxRes
);</p>
<p>&nbsp;&nbsp;&nbsp; end;</p>
<p>&nbsp;&nbsp;&nbsp;
最后重新编译包。如果安装成功的话，让我们来测试一下。新建一个窗体，添加一个ActionList，再放上一个TimageList控件，指定
ActionList的Images属性为Imagelist1。然后添加Listbox
Action，如图3.24所示，你会发现Action的右边都有一个漂亮的图标，并且在没有Contructor的情况下，Action的属性都设定了
正确的缺省属性值。除此之外，即使ImageList中已经有了图像，Delphi也会自动复制Action资源中的图标到Imagelist，并能很智
能地调节Action的ImageIndex。到此为止，我们才算真的大功告成了。</p>
</div>
<br> <img src ="http://www.cppblog.com/WangYu/aggbug/42559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-06 02:45 <a href="http://www.cppblog.com/WangYu/articles/42559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Delphi ActionList详解</title><link>http://www.cppblog.com/WangYu/articles/42554.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Tue, 05 Feb 2008 17:30:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42554.html</guid><description><![CDATA[<table align="center" border="1" bordercolor="#aecaae" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td style="font-size: 18px;" bordercolor="#FFFFFF" align="center" height="60" valign="bottom" width="100%"><strong> <font color="#003399" face="楷体_GB2312" size="5"><strong>Delphi ActionList详解<br>
            </strong></font></strong> </td>
        </tr>
        <tr>
            <td bordercolor="#FFFFFF" align="center" width="100%">
            <hr align="center" noshade="noshade" size="1" width="100%">
            </td>
        </tr>
        <tr>
            <td style="font-size: 9pt;" bordercolor="#FFFFFF" align="center" width="100%">发表日期：2006年7月4日&nbsp;&nbsp;
            &nbsp;&nbsp;
            &nbsp;&nbsp;已经有498位读者读过此文</td>
        </tr>
        <tr>
            <td bordercolor="#FFFFFF" align="center" width="100%">
            <table style="table-layout: fixed;" align="center" border="0" cellpadding="0" cellspacing="0" width="98%">
                <tbody>
                    <tr>
                        <td align="center" width="100%"> <br></td>
                    </tr>
                    <tr>
                        <td> <font class="news"><br>
                        一个友好的用户界面,必须具有下拉菜单，弹出菜单，工具条和快捷键。同样一个功能，程序员可能要提供几种操作方式，如文本拷贝，菜单命令&amp;
                        Copy,快捷键Ctrl+C,工具条上的拷贝按钮，都是程序员提供给用户的操作，可以大大方便了不同层次的用户。但是，多增加一种操作方式，就意味着增
                        加响应事件的代码，还有，实现统一功能的多个操作必须一致，如剪切板上不为空的时候，菜单命令，快捷键，按钮，都是可以选择的Enabled状态，而剪切
                        板上没有内容时，这些构件的Enabled状态都为假。当然，如果不集中控制这些细节的话，实现相同功能的构件要一致，必须多增加代码，控制较为复杂。<br>Action
                        就是将实现某个功能的细节统一管理起来，如Caption, Checked, Enabled, HelpContext, Hint,
                        ImageIndex, ShortCt, Visible,
                        onExecute等属性和事件。程序员设置Action属性的同时,还要实现执行Action的代码onExecute。定义了Action后，将其连
                        接到一个菜单项，或者按钮上时，Action定义的属性和OnExecute事件，马上取代了原来的设置，并且建立了连接。通过这个连接，当Action
                        的属性发生变化时，如Enabled发生变化，那么与之连接的构件属性都会自动更新；反过来，当构件对应的事件被执行时，Action的
                        onExecute也被调用。<br>8.1.2 Action 和ActionList<br>Action是定义了最小的功能单位，使用它来在不同构件间，达到代码集中管理。表4-12定义了Action的一些属性：<br>ActionList
                        是将多个Action统一管理的构件，在窗体设计阶段，使用它的弹出菜单，可以增减Action和定义Action的属性。它本身的属性Image连接了
                        一个TImageList,指出它所包含的Action使用那个TImageList来定义ImageIndex。ActionLink是一个看不见的构
                        件，但是它是负责构件和Action之间建立连接的。<br>表 4-12 &nbsp;Action属性和事件<br>属性和事件 &nbsp;具体功能<br>Caption &nbsp;标题，可用于菜单项，ToolButton等。<br>Category &nbsp;分类<br>Checked &nbsp;设置选择状态，用于菜单项<br>Enabled &nbsp;设置可用状态，用于与之连接的所有构件<br>HelpContext &nbsp;帮助的索引项，用于与之相连的所有构件<br>Hint &nbsp;智能感知，设置提示内容<br>ImageIndex &nbsp;设置菜单项和按钮上的图片<br>Visible &nbsp;指示与之连接的构件可否显示<br>ShortCut &nbsp;设置菜单项的快捷键。<br>OnExecute &nbsp;执行此Action的代码<br>OnHint &nbsp;当被感知的构件显示Hint时调用的代码<br>OnUpdate &nbsp;当Action更新构件的属性时调用的代码<br>////////////<br>我们先来认识TActionList。TActionList 是从TCustomAction中继承来的，由它管理定义好的Action，是程序员在对Action编程的接口。<br>新
                        建一个工程文件，在主窗体上增加一个TActionList，一个TMemo，一个TImageList，几个按钮，几个菜单项。双击
                        TActionlist，会出现它的编辑界面。再单击右键选择New standard
                        action，显示出预定义的Action类别，属性Category是用来表明Action所属类别，我们可以根据需要选择具体的类别，如图4-10所
                        示，我们引入了文件操作的一些标准Action。<br>这里所列的Action，大部分属性已经定义好了，就是说只要把他们添加到ActionList中，就可以使用。同样的，也可以新建Action，在Category属性中加上自己定义的名字，之后再添加新的Action 。<br>8.2.2 连接Action<br>定义了Action后，不与菜单项和按钮连接，是不能使用的，也没有达到程序功能集中实现和管理。使用对象观察器，看看我们刚刚选择的菜单项和按钮上是否有属性Action。<br>在
                        这些构件的Action属性内，选择一个Action之后，构件本身的很多属性都随着Action定义的属性发生了变化，例如菜单项的Caption与
                        Action的属性Caption就一致了，构件的属性Name和Tag是不随着改变的。连接后，Action的属性值被拷贝到所连接构件的相应属性上，
                        这种连接是动态地，当Action的属性值发生变化时，自动更新所联系的构件。<br>注意：如果使用的是一个TToolButton 或着是菜单项，必须自己设置与它相联系的Images属性，虽然构件的ImageIndex属性已经动态的同Action相连接，但是属性Images并未自动连接。<br>继续本节的例子，通过设置菜单项和按钮的Action属性，完成Action的连接。<br>8.2.3 处理Action<br>处
                        理Action的代码可放置在Action的onExecute事件，也可以集中放置在ActionList的onExecuteAction中，还可以
                        更集中的放置在Application的onExecuteAction事件中。改变了以往每个构件都必须对应事件的做法，程序设计上更系统化。<br>在本节的例子中，编写某几个Action的onExecute方法。如FileOpenCmd的onExecute事件如下：<br>if OpenDialog.Execute then<br>&nbsp; &nbsp;Memo1.LoadFromFile(OpenDialog.FileName);<br>8.2.3 更新Action属性<br>Action
                        的事件有OnExecute和OnUpdate，OnExecute事件在控制被触发时响应，比如说按钮被按下，菜单被按下，而OnUpdate事件是在
                        应用程序空闲时被调用，用来更新Action的属性。ActionList 中也有OnUpdate,
                        可以用作设置菜单或者按钮的可选等属性使用。如下例,这个例子来自于$Delphi\Demos\RichEdit\Richedit.Dpr程序，有兴
                        趣的读者可查看一下，以便更了解Action。<br>1 &nbsp; &nbsp;procedure TMainForm.ActionList2Update(Action: TBasicAction; var Handled: Boolean);<br>2 &nbsp; &nbsp;begin<br>3 &nbsp; &nbsp; &nbsp;EditCutCmd.Enabled := Editor.SelLength &gt; 0;<br>4 &nbsp; &nbsp; &nbsp;EditCopyCmd.Enabled := EditCutCmd.Enabled;<br>5 &nbsp; &nbsp; &nbsp;if Editor.HandleAllocated then<br>6 &nbsp; &nbsp; &nbsp; &nbsp; begin<br>7 &nbsp; &nbsp; &nbsp; &nbsp; EditUndoCmd.Enabled := Editor.Perform(EM_CANUNDO, 0, 0) &lt;&gt; 0;<br>8 &nbsp; &nbsp; &nbsp; &nbsp; EditPasteCmd.Enabled := Editor.Perform(EM_CANPASTE, 0, 0) &lt;&gt; 0;<br>9 &nbsp; &nbsp; &nbsp; &nbsp; end;<br>10 &nbsp; end;<br>当应用程序空闲时，就执行这段代码，首先检查是否有被选定的文本，如果有则将Cut和Copy的Enabled属性设为True；当前控制是Editor时，设置Undo和Paste属性，Perfom是构件向自身发送消息的方法。<br>Delphi
                        已经定义了一些标准的Action，这些Action实现的功能和VCL代码已经结合起来。所以，程序员可以直接使用，有些Action甚至不需要定义
                        onExecute事件。如Cut，Copy，Paste等剪切板操作，Cancel，Delete，Edit，First，Insert，Last，
                        Next，Prior，Post，Refresh等数据导航功能，Arrange，Cascade，Close，MinimizeAll，
                        TileHorizontal等窗口排布功能。&nbsp;&nbsp;<br>
                        </font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td bordercolor="#FFFFFF" height="25" width="100%">
            <div align="center">  </div>
            <br></td>
        </tr>
    </tbody>
</table>
<br><img src ="http://www.cppblog.com/WangYu/aggbug/42554.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-06 01:30 <a href="http://www.cppblog.com/WangYu/articles/42554.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈Object Pascal的指针(转自CSDN)</title><link>http://www.cppblog.com/WangYu/articles/42525.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Mon, 04 Feb 2008 13:24:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42525.html</guid><description><![CDATA[浅谈Object Pascal的指针 <br><br><br><br>大家都认为，C语言之所以强大，以及其自由性，很大部分体现在其灵活的指针运用
上。因此，说指针是C语言的灵魂，一点都不为过。同时，这种说法也让很多人
产生误解，似乎只有C语言的指针才能算指针。Basic不支持指针，在此不论。其实，Pascal语言本身也是支持指针的。从最初的Pascal发展至今
的Object Pascal，可以说在指针运用上，丝毫不会逊色于C语言的指针。 <br><br>以下内容分为八个部分，分别是 <br>一、类型指针的定义 <br>二、无类型指针的定义 <br>三、指针的解除引用 <br>四、取地址（指针赋值） <br>五、指针运算 <br>六、动态内存分配 <br>七、字符数组的运算 <br>八、函数指针 <br><br><br>一、类型指针的定义。对于指向特定类型的指针，在C中是这样定义的： <br>int *ptr; <br>char *ptr; <br>与之等价的Object Pascal是如何定义的呢？ <br>var <br>ptr : ^Integer; <br>ptr : ^char; <br>其实也就是符号的差别而已。 <br><br>二、无类型指针的定义。C中有void *类型，也就是可以指向任何类型数据的指针。Object Pascal为其定义了一个专门的类型：Pointer。于是， <br>ptr : Pointer; <br>就与C中的 <br>void *ptr; <br>等价了。 <br><br>三、指针的解除引用。要解除指针引用（即取出指针所指区域的值），C 的语法是 (*ptr)，Object Pascal则是 ptr^。 <br><br>四、取地址（指针赋值）。取某对象的地址并将其赋值给指针变量，C 的语法是 <br>ptr = &amp;Object; <br>Object Pascal 则是 <br>ptr := @Object; <br>也只是符号的差别而已。 <br><br>五、指针运算。在C中，可以对指针进行移动的运算，如： <br>char a[20]; &nbsp; <br>char *ptr=a; &nbsp; <br>ptr++; <br>ptr+=2; <br>当执行ptr++;时，编译器会产生让ptr前进sizeof(char)步长的代码，之后，ptr将指向a[1]。ptr+=2;这句使得ptr前进两 个sizeof(char)大小的步长。同样，我们来看一下Object Pascal中如何实现： <br>var <br>&nbsp; a : array [1..20] of Char; <br>&nbsp; ptr : PChar; //PChar 可以看作 ^Char <br>begin <br>&nbsp; ptr := @a; <br>&nbsp; Inc(ptr); // 这句等价于 C 的 ptr++; <br>&nbsp; Inc(ptr, 2); //这句等价于 C 的 ptr+=2; <br>end; <br><br>六、动态内存分配。C中，使用malloc()库函数分配内存，free()函数释放内存。如这样的代码： <br>int *ptr, *ptr2; <br>int i; <br>ptr = (int*) malloc(sizeof(int) * 20); <br>ptr2 = ptr; <br>for (i=0; i&lt;20; i++){ <br>&nbsp; *ptr = i; <br>&nbsp; ptr++; <br>} <br>free(ptr2); <br>Object
Pascal中，动态分配内存的函数是GetMem()，与之对应的释放函数为FreeMem()（传统Pascal中获取内存的函数是New()和
Dispose()，但New()只能获得对象的单个实体的内存大小，无法取得连续的存放多个对象的内存块）。因此，与上面那段C的代码等价的
Object Pascal的代码为： <br>var ptr, ptr2 : ^integer; <br>&nbsp; i : integer; <br>begin <br>&nbsp; GetMem(ptr, sizeof(integer) * 20); <br>&nbsp; &nbsp; //这句等价于C的 ptr = (int*) malloc(sizeof(int) * 20); <br>&nbsp; ptr2 := ptr; //保留原始指针位置 <br>&nbsp; for i := 0 to 19 do <br>&nbsp; begin <br>&nbsp; &nbsp; ptr^ := i; <br>&nbsp; &nbsp; Inc(ptr); <br>&nbsp; end; <br>&nbsp; FreeMem(ptr2); <br>end; <br>对
于以上这个例子（无论是C版本的，还是Object
Pascal版本的），都要注意一个问题，就是分配内存的单位是字节（BYTE），因此在使用GetMem时，其第二个参数如果想当然的写成
20，那么就会出问题了（内存访问越界）。因为GetMem(ptr,
20);实际只分配了20个字节的内存空间，而一个整形的大小是四个字节，那么访问第五个之后的所有元素都是非法的了（对于malloc()的参数同
样）。 <br><br>七、字符数组的运算。C语言中，是没有字符串类型的，因此，字符串都是用字符数组来实现，于是也有一套str打头的库函数以进行字符数组的运算，如以下代码： <br>char str[15]; <br>char *pstr; <br>strcpy(str, "teststr"); <br>strcat(str, "_testok"); <br>pstr = (char*) malloc(sizeof(char) * 15); <br>strcpy(pstr, str); <br>printf(pstr); <br>free(pstr); <br>而
在Object
Pascal中，有了String类型，因此可以很方便的对字符串进行各种运算。但是，有时我们的Pascal代码需要与C的代码交互（比如：用
Object Pascal的代码调用C写的DLL或者用Object
Pascal写的DLL准备允许用C写客户端的代码）的话，就不能使用String类型了，而必须使用两种语言通用的字符数组。其实，Object
Pascal提供了完全相似C的一整套字符数组的运算函数，以上那段代码的Object Pascal版本是这样的： <br>var str : array [1..15] of char; <br>&nbsp; pstr : PChar; //Pchar 也就是 ^Char <br>begin <br>&nbsp; StrCopy(@str, 'teststr'); //在C中，数组的名称可以直接作为数组首地址指针来用 <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //但Pascal不是这样的，因此 str前要加上取地址的运算符 <br>&nbsp; StrCat(@str, '_testok'); <br>&nbsp; GetMem(pstr, sizeof(char) * 15); <br>&nbsp; StrCopy(pstr, @str); <br>&nbsp; Write(pstr); <br>&nbsp; FreeMem(pstr); <br>end; <br><br>八、函数指针。在动态调用DLL中的函数时，就会用到函数指针。假设用C写的一段代码如下： <br>typedef int (*PVFN)(int); //定义函数指针类型 <br>int main() <br>{ <br>&nbsp; HMODULE hModule = LoadLibrary("test.dll"); <br>PVFN pvfn = NULL; <br>&nbsp; pvfn = (PVFN) GetProcAddress(hModule, "Function1"); <br>&nbsp; pvfn(2); <br>&nbsp; FreeLibrary(hModule); <br>} <br>就我个人感觉来说，C语言中定义函数指针类型的typedef代码的语法有些晦涩，而同样的代码在Object Pascal中却非常易懂： <br>type PVFN = Function (para : Integer) : Integer; <br>var <br>&nbsp; fn : PVFN; <br>&nbsp; &nbsp; //也可以直接在此处定义，如：fn : function (para:Integer):Integer; <br>&nbsp; hm : HMODULE; <br>begin <br>&nbsp; hm := LoadLibrary('test.dll'); <br>&nbsp; fn := GetProcAddress(hm, 'Function1'); <br>&nbsp; fn(2); <br>&nbsp; FreeLibrary(hm); <br>end;
<br>  <img src ="http://www.cppblog.com/WangYu/aggbug/42525.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-04 21:24 <a href="http://www.cppblog.com/WangYu/articles/42525.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>