星期五, 11月 30, 2007

能自動關閉的訊息視窗

加到common.pas內

procedure ShowMsg(const STitle,SText:String; const ITimeOut:Integer);
var
aFrm:TForm2;
begin
aFrm:=TForm2.Create(nil);
aFrm.Caption := STitle;
aFrm.Label1.Caption := SText;
aFrm.Timer1.Interval := ITimeOut;
try
aFrm.ShowModal;
finally
aFrm.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMsg('訊息測試','訊息內容................',4000);
end;

新增1個TButton,1個TLabel,2個Timer(1個顯示用interval用1000,2個enabled用false,並都加入事件)

unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;

type
TForm2 = class(TForm)
Timer1: TTimer;
Label1: TLabel;
Button1: TButton;
Timer2: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
i : integer;
{ Private declarations }
public
{ Public declarations }
end;

var
Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormCreate(Sender: TObject);
begin
i := Timer1.Interval div 1000;
Button1.Caption := 'OK ('+inttostr(i)+')';
Timer2.Enabled := true;
Timer1.Enabled := true;
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
Timer2.Enabled := false;
Timer1.Enabled := false;
self.close;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
self.close;
end;

procedure TForm2.Timer2Timer(Sender: TObject);
begin
Dec(i);
Button1.Caption := 'OK ('+inttostr(i)+')';
end;

end.

星期一, 11月 26, 2007

thread用子覆蓋方式去執行各種方法


unit Unit2;

interface

uses
Classes, StdCtrls, SysUtils, Windows, Messages, Dialogs;

const
WM_IN = WM_USER + 1;
WM_OUT = WM_USER + 2;

type
TMyThread = class(TThread)
private
num : integer;
Lb1 : TLabel;
//hd : THandle;
procedure Download;
//procedure DoVisible;
//procedure showVisible(Lbt: TLAbel; B: Integer);
{ Private declarations }
protected
procedure calculate(A: Integer); virtual; abstract;
procedure Execute; override;
public
constructor Create(lbc: TLabel; fund_name : string; sn:integer);
end;

TinFund = class(TMyThread)
protected
procedure calculate(C : Integer); override;
end;
ToutFund = class(TMyThread)
protected
procedure calculate(C : Integer); override;
end;

implementation

uses Unit1;

constructor TMyThread.Create(lbc: TLabel; fund_name : string; sn:integer);
begin
//idhttp去抓sl
//Lb1 := TLabel.Create(nil);
num := sn;
Lb1 := lbc;
//hd:=hhd;
FreeOnTerminate := True;
inherited Create(False);
end;

{
procedure TMyThread.DoVisible;
begin
Lb1.caption := inttostr(num);
end;


procedure TMyThread.showVisible(Lbt: TLAbel; B : Integer);
begin
Lb1 := Lbt;
num := B;
Synchronize(DoVisible);
end;
}
procedure ToutFund.calculate(C : Integer);
var
i : integer;
begin
for i:=1 to C do
begin
//PostMessage(hd,WM_OUT,i,0);
//if Terminated then Exit;
if Assigned(Lb1) then
Lb1.caption := inttostr(i);

end;
ShowMessage('kobe');
end;

procedure TinFund.calculate(C : Integer);
var
i : integer;
begin
for i:=1 to 5000 do
begin
//if Terminated then Exit;
if Assigned(Lb1) then
Lb1.caption := inttostr(i);
//PostMessage(hd,WM_IN,i,0);


end;
end;

procedure TMyThread.Execute;
begin
Download;
calculate(num);
{ Place thread code here }
end;

procedure TMyThread.Download;
begin

end;



end.





unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, AppEvnts;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Label1: TLabel;
Button2: TButton;
Edit2: TEdit;
Label2: TLabel;
ApplicationEvents1: TApplicationEvents;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
private
ThreadsRunning : integer;
procedure ThreadDoe(Sender: TObject);
procedure ThreadDoe1(Sender: TObject);
{ Private declarations }
public
{ Public declarations }
end;


var
Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.ThreadDoe(Sender: TObject);
begin
Dec(ThreadsRunning); //減到為0又可以可以重跑
if ThreadsRunning = 0 then
begin
Button1.Enabled := True; //Thread跑完後
Button2.Enabled := True; //Thread跑完後
end;
end;

procedure TForm1.ThreadDoe1(Sender: TObject);
begin
//Dec(ThreadsRunning); //減到為0又可以可以重跑
Button2.Enabled := True; //Thread跑完後
end;

procedure TForm1.Button1Click(Sender: TObject);
var
fund_name : string;
sn : integer;
begin
ThreadsRunning := 2;//2個Thread會同時跑

button1.enabled:=false;
//TDownLoad.Create.OnTerminate := ThreadDoe;
fund_name := 'kobe1';
sn := 100000;
TinFund.Create(Label1, fund_name, sn).OnTerminate := ThreadDoe;

button2.enabled:=false;
sn := 500000;
ToutFund.Create(Label2, fund_name, sn).OnTerminate := ThreadDoe;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
fund_name : string;
sn : integer;
begin
button2.enabled:=false;
sn := 5000000;
ToutFund.Create(Label2, fund_name, sn).OnTerminate := ThreadDoe1;

end;

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
{
if Msg.message = WM_IN then
Label1.Caption:=IntToStr(Msg.wParam);
if Msg.message = WM_OUt then
Label2.Caption:=IntToStr(Msg.wParam);
}
end;

end.

讓視窗程式開啟時,位置在螢幕的中間偏上

通常是設定在position屬性=poScreenCenter,但是那個是正中間,對於一般人的眼睛視線應該是中間偏上比較息慣,所以你可以在FormShow時加入下列程式碼:


procedure TForm1.FormShow(Sender: TObject);
begin
left := (screen.Width - Width) div 2; //等寬
top := (screen.Height - Height)*1 div 3; //高度偏上
end;

星期五, 11月 23, 2007

用執行序去跑ADO資料庫更新


unit Unit1;

uses unit2;

procedure TForm1.Button3Click(Sender: TObject);
begin
jjj := strtoint(Edit6.text); //jjj為記錄資料庫塞爆的全域變收

button1.enabled:=false;
TDownLoad.Create.OnTerminate := ThreadDoe;
end;

procedure TForm1.ThreadDoe(Sender: TObject);
begin
//Dec(ThreadsRunning); //減到為0又可以可以重跑 //如果TDownLoad.Create很多個同時
Button1.Enabled := True; //Thread跑完後
end;



unit Unit2;

interface

uses
Classes, ADODB, Dialogs, Forms;//這裡也使用Forms不太好

type
TDownload = class(TThread)
private
procedure Download;
{ Private declarations }
protected
procedure Execute; override;
public
constructor Create;
end;

implementation

uses Unit1;

{ TDownload }
constructor TDownload.Create;
begin
FreeOnTerminate := True;
inherited Create(False);
end;

procedure TDownload.Execute;
begin
Download;
{ Place thread code here }
end;

procedure TDownload.Download;
var
i : integer;
query1 : string;
FADOQuery : TADOQuery;
FADOConn : TADOConnection;
begin

//showmessage(Edit3.text);
FADOConn := TADOConnection.Create(nil);
FADOQuery:=TADOQuery.Create(nil);
FADOConn.ConnectionString := svr_string;
FADOConn.Connected := True;

FADOQuery.Connection := FADOConn;
try
try
for i := 0 to jjj - 1 do
begin
if not SQLExecuteOK(FADOQuery, 'insert into test (k) values (3)') then
ShowMessage('Query ERROR!!');
Application.ProcessMessages;

end;
except
showmessage('error');
end;
finally
FADOQuery.free;
FADOConn.Free;

end;
end;

end.

星期四, 11月 22, 2007

一個TLabel文字用不同顏色

首先在Label1的Caption文字設定 '台灣股票 漲▲▼ 36.52'
然後在你要變化的事件加入

Label1.Canvas.Font.Color := clBlue;
Label1.Canvas.textout(Label1.Canvas.TextWidth('台灣股票 漲'),0,'▲');

//另外一種寫法,記得前面不可用Label1.Caption否則要按兩次才可以顯示
Label1.Canvas.TextOut(0,0,'台灣股票 漲▲546.3');
Label1.canvas.Font.Color:=RGB(255,45,45);
Label1.Canvas.TextOut(Label1.Canvas.TextWidth('台灣股票 漲'),0,'▲');

星期一, 11月 19, 2007

ElTree使用


procedure TForm1.FormShow(Sender: TObject);
var
old_dept: string;
i: integer;
Node,NewNode:TELTreeItem;
begin
VirtualTable1.Filtered:=True;
VirtualTable1.Active:=True;
Node:=nil; // 避免編譯時出現警告訊息
ElTree1.Selected:=Nil;
ElTree1.Items.Clear();
ElTree1.Items.BeginUpdate();
try
old_dept := #13#10;
VirtualTable1.First;
for i:=0 to VirtualTable1.RecordCount-1 do
begin
if VirtualTable1.FieldByName('bank').AsString <> old_dept then
begin
Node:=ElTree1.Items.Add(Nil,VirtualTable1.FieldByName('bank').AsString);
Node.ShowCheckBox:= True ;
//Node.CheckBoxType := ectCheckBox ;
//Node.CheckBoxEnabled := True ;
Node.UseStyles := True ;
Node.MainStyle.OwnerProps := False;
Node.MainStyle.FontSize:=10;
Node.MainStyle.FontName:=Screen.MenuFont.Name;
//Node.ImageIndex:=13;
old_dept:=VirtualTable1.FieldByName('bank').AsString;
end;
NewNode:=ElTree1.Items.AddChild(Node,VirtualTable1.FieldByName('accout').AsString);
NewNode.ColumnText.Add(VirtualTable1.FieldByName('money').AsString);
//NewNode.ShowCheckBox:= True ;
//NewNode.CheckBoxType := ectCheckBox ;
//NewNode.CheckBoxEnabled := True ;
NewNode.UseStyles := True ;
NewNode.MainStyle.OwnerProps := False;
NewNode.MainStyle.FontSize:=10;
NewNode.MainStyle.FontName:=Screen.MenuFont.Name;
//NewNode.ImageIndex:=12;

VirtualTable1.Next;
end;
finally
ElTree1.Items.EndUpdate();
end;

end;

procedure TfrmMain.TreeItemFocused(Sender: TObject);
begin
//顯示所按的標籤文字
//Caption:=ElTree1.ItemFocused.text;
//顯示他爸爸的標籤文字
if ElTree1.itemFocused.Parent <> nil then
Caption:=ElTree1.ItemFocused.Parent.text;
//顯示第一個結點的標籤文字
//Caption := ElTree1.Items.GetFirstNode.Text;
//顯示所有結點的數目
//Caption := inttostr(ElTree1.Items.Count);
end;

(2)說明
ElTree1.Items.GetFirstNode 返回TREEVIEW的第一個節點,函數類型為
:TTreeNode
ElTree1.Items.Count 返回當前TreeView的全部節點數,整數
ElTree1.Selected.Level 返回當前選中節點的在目錄樹中的級別,
根目錄為0
ElTree1.Selected.Parent 返回當前選中節點上級節點,函數類型為
:TTreeNode

參考網址
http://www.delphibbs.com/keylife/iblog_show.asp?xid=19823

星期五, 11月 16, 2007

日期相差多少年、月、日、時、分、秒


procedure TForm1.BitBtn1Click(Sender: TObject);
var
a,b: Tdatetime;
c: string;
begin
a:=2007/3/13;
b:=2006/3/2;
c := '';
if (daysbetween(a,b) div 30) <> 0 then
c := inttostr(daysbetween(a,b) div 30)+'個月';

if ((daysbetween(a,b) mod 30) <> 0)and ((daysbetween(a,b) div 30) <> 0)then
c := c + '又' + inttostr(daysbetween(a,b) mod 30)+'天'
else if (daysbetween(a,b) mod 30) <> 0 then
c := inttostr(daysbetween(a,b) mod 30)+'天';

showmessage(c);
showmessage(inttostr(yearsbetween(a,b))+'年');
showmessage(inttostr(monthsbetween(a,b))+'月');
showmessage(inttostr(daysbetween(a,b))+'天');
showmessage(inttostr(hoursbetween(a,b))+'小時');
showmessage(inttostr(minutesbetween(a,b))+'分鐘');
showmessage(inttostr(secondsbetween(a,b))+'秒');
end;

星期三, 11月 14, 2007

把idhttp寫成一個執行序


1、在idhttp.OnWork事件裡加Application.ProcessMessages;
在窗體上放個idhttp控件,寫他的OnWork方法。
procedure TForm1.IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
Application.ProcessMessages;
end;

2、
//在主窗體中定義一個線程類
type
TMyDownLoad=class(TThread)
protected
procedure Execute;override;
procedure Download;
end;

type
TFMain = class(TForm)
....

procedure TMyDownLoad.Download;
Var
UnitName,PathName:String;
MyStream:TMemoryStream;
filepath:string;
IDHTTP: TIDHttp;
begin
IDHTTP:= TIDHTTP.Create(nil);
MyStream:=TMemoryStream.Create;
try
IdHTTP.Get('http://127.0.0.1/aiyouasp/testcode/11.exe',MyStream);
except
showmessage('網絡出錯未能下載完成!');
MyStream.Free;
Exit;
end;
filepath:=ExtractFilePath(ParamStr(0));
MyStream.SaveToFile(filepath+'\DownLoadFiles\11.exe');
MyStream.Free;
showmessage('下載完成!');
end;
procedure TMyDownLoad.Execute
begin
inherited;
Download;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TMyDownLoad.Create(false);
end;

星期四, 11月 08, 2007

如何使TProgressBar與TStatusBar結合在一起



新增1個TButoon、1個TProgressBar及1個TStatusBar
事件加入FormCreate及OnDrawPanel

procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;
begin
ProgressBar1.Position := 0;
ProgressBar1.Max := 100;

for i := 0 to 100 do
begin
ProgressBar1.Position := i;
Sleep(25);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
ProgressBarStyle : integer;
begin
//將狀態列的第二塊面板設為的自繪(即psOwnerDraw)
StatusBar1.Panels[1].Style := psOwnerDraw;

//將進程條放入狀態列
ProgressBar1.Parent := StatusBar1;

//去除狀態列的邊框,這樣就與狀態列溶為一體了
ProgressBarStyle := GetWindowLong(ProgressBar1.Handle,GWL_EXSTYLE);
ProgressBarStyle := ProgressBarStyle - WS_EX_STATICEDGE;
SetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE, ProgressBarStyle);
end;

procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel;
const Rect: TRect);
begin
progressbar1.BoundsRect:=rect;
end;

CheckBox、RadioButton、ListBox

CheckBox
可以拿來做複選的方式。重要屬性如下:
checked : true 或 false ,gray及unchecked皆為 false,而 checked 為 true。
state : 如果 allowgrayed為true,則有三種狀態
type TCheckBoxState = (cbUnchecked, cbChecked, cbGrayed);
allowgrayed:是否需要灰階的選項
常用程式碼

function showState(state: TCheckBoxState):string;
begin
case state of
cbUnchecked : result:='unchecked';
cbChecked : result:='checked';
cbGrayed : result:='grayed';
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
checkbox1.AllowGrayed:= not checkbox1.AllowGrayed;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if checkbox1.Checked=true then
showmessage('checkbox1 = true')
else
showmessage('checkbox1 = false');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
showmessage(showState(checkbox1.state));
end;



RadioButton
常放在Panel、RadioBox及GroupBox中,才能形成一組多選一的狀態

//先選擇一組內的所有Radio(shift選取),然後選擇共同的事件
var
blood : string;//blood為全域變數
procedure TForm1.RadioButton1Click(Sender: TObject);
var i: integer;
begin
blood:=Tradiobutton(sender).Caption + '型';
end;
//最後確定的按鈕就為
procedure TForm1.Button2Click(Sender: TObject);
begin
showmessage( blood + #10); //#10多加(自己亂加的)跳行意思
end;
//動態加入radiobuttoon於radiogoup中
procedure TForm1.Button1Click(Sender: TObject);
begin
radiogroup1.Items.Add(edit1.Text);
end;
//將剛剛動態加入radiobuttoon清除
procedure TForm1.Button3Click(Sender: TObject);
begin
radiogroup1.Items.clear;
end;
//將剛剛動態加入radiobuttoon清除
procedure TForm1.Button4Click(Sender: TObject);
begin
radiogroup1.Columns:=2;
end;



ListBox

//單選,並得知ListBox1選哪一個
procedure TForm1.Button1Click(Sender: TObject);
begin
listbox1.MultiSelect:=false;
showmessage(listbox1.Items[listbox1.ItemIndex]);
end;
//排序或不排序,一直做反向處理
procedure TForm1.Button3Click(Sender: TObject);
begin
listbox1.Sorted:=not listbox1.Sorted;
end;
//加新的Item進去
procedure TForm1.aa1Click(Sender: TObject);
begin
listbox1.AddItem(edit1.Text ,nil );
end;
//刪除Item
procedure TForm1.delete1Click(Sender: TObject);
begin
listbox1.DeleteSelected;
end;
//顯示所有選到,這是可複選才要喔
procedure TForm1.showSelected1Click(Sender: TObject);
var
i: integer;
s: string;
begin
for i:= 0 to listbox1.Items.Count -1 do
begin
if listbox1.Selected[i] then
s:= s + ' ' + listbox1.Items.Strings[i];
end;



ComboBox
重要屬性
items
text
maxLength
dropDownCount
style
autocomplete
autodropdown//自動下拉到你要的

procedure TForm1.ComboBox1Click(Sender: TObject);
//加入一個新的Item
begin
listbox1.Items.Add(combobox1.Text );
end;

星期三, 11月 07, 2007

換好看的介面囉

最好看的商業界面元件
BSF BusinessSkinForm
http://www.2ccc.com/article.asp?articleid=4436

換膚元件
VCLSkin Skinpack
http://www.cnblogs.com/support/archive/2007/05/10/741878.html

指標的使用


procedure TForm1.Button1Click(Sender: TObject);
var
P: ^Integer; //P為一個指標,指標內存變數為Integer
X: Integer;
begin
P := @X; //用@ 符號把另一個相同類型變數的地址賦給它
// 改變此位址的值有兩種方法
X := 10;
P^ := 20;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
P: ^Integer;
begin
// initialization
New (P); //動態分配內存
// operations
P^ := 20;
ShowMessage (IntToStr (P^));
// termination
Dispose (P); //記得在此釋放喔
end;

procedure TForm1.Button3Click(Sender: TObject);
var
P: ^Integer;
begin
P := nil; //空指標如果還要顯示,那要加nil,否則會出現"一般保護錯"(GPF)的錯誤
ShowMessage (IntToStr (P^));
end;

procedure TForm1.Button4Click(Sender: TObject);
var
P: ^Integer;
X: Integer;
begin
P := @X;
X := 100;
if P <> nil then //所以結論用此來顯示,會比較安全
ShowMessage (IntToStr (P^));
end;

星期二, 11月 06, 2007

元件一起設定屬性(事件)

按Shift然後選所有要更動的元件,然後你就可以一起設定他們的屬性或事件囉!!

Parent ,Owner ,Sender ,Self ,is ,as 的用法

甚麼是Parent?
Parent 就是直接包含物件本身的 container (容器)。
例如:在 form1上加上一個 button1,則 button1的 parent就是 form1,若在 form1上加上一個 panel1,然後在 panel1上再加上一個 button2,則 panel1的 parent就是 form1,而 button2 的 parent 就是 panel1。

甚麼是 Owner ?
Owner 表示物件的擁有者,owner主要負責兩件事,一是owner的記憶體空間被移除時,其所擁有的物件亦將同時被移除。二是 owner須負責載入及儲存 被擁有者的published屬性。
通常一個表單 form擁有在其上的所有元件,所以表單被移除時,其上面的元件之記憶體空間亦將被移除。
一個應用程式則擁有所有的表單,所以應用程式結束時,所有元件擁有的記憶體亦將移除。

甚麼是 sender ?
Sender就是觸發這個事件的物件。
例如:
在 form1上加上四個 button (button1, button2, button3, button4) 及一個 edit1。
button1的 onclick事件加上指令如下:

procedure TForm1.Button1Click(Sender: TObject);
begin
if sender is Tbutton then
showmessage(Tbutton(sender).Caption)
else if sender = nil then
showmessage('no sender')
else if sender = edit1 then
showmessage('from edit1');
end;

button2 的 onclick事件,點選 Button1click。
button3 的 onclick 事件,寫上下列指令

procedure TForm1.Button3Click(Sender: TObject);
begin
button1Click(nil);
end;

button4 的 onclick 事件,寫上下列指令

procedure TForm1.Button4Click(Sender: TObject);
begin
button1click(edit1);
end;

edit1 的 onclick 事件,點選 Button1Click。
F9執行後,分別點選button1, button2, button3, button4, edit1,觀察其結果。

甚麼是 is ?
is 是一個運算子,用來判斷一個物件是否屬於某一種類別或其繼承者。
語法: object is class
例如: if sender is Tedit then ...
if sender is Tobject then .... //只要是物件,則永遠成立,因所有物件皆繼承 Tobject。

甚麼是 as ?
as 是一個運算子,可以將一個物件指定成某一種類別,若型態錯誤,將傳回 EInvalidCast之例外。
例如:showmessage((sender as Tbutton).caption); // 如果 sender 在執行階段為 Tbutton 則可顯示caption屬性。

甚麼是 self?
Self是指目前程式碼所屬的物件。
例如:
procedure TForm1.Button1Click(Sender: TObject);
begin
self.color:=clred; // self 表示 form1
end;

星期五, 11月 02, 2007

判斷資料表是否存在



Function TableExist( pConn:TADOConnection; pcTable : string ) : boolean ; overload ;
var
tmpFldList : TStrings ;
nLoop : integer ;
begin
Result := False ;
tmpFldList := TStringList.Create ;
pConn.GetTableNames( tmpFldList, True ); // 包含系統表
for nLoop := 0 to tmpFldList.Count - 1 do
begin
if uppercase( tmpFldList[nLoop] ) = uppercase( pcTable ) then
begin
Result := True ;
break ;
end;
end;
tmpFldList.Free ;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin

if TableExist(ADOConnection1, '富達新興') then
showmessage('ok');
end;

判斷某一日期是否小於現在


procedure TForm1.Button1Click(Sender: TObject);
var
pDate : AnsiString;
begin
pDate:='2007/11/01';
if strtodate(pDate) < strtodate(FormatDateTime('yyyy/mm/dd', Now)) then
showmessage(datetostr(now()));
end;

星期四, 11月 01, 2007

如何快速的跳到所要的函式位置

通常要追蹤函式位置時,會先按Ctrl然後點函式名字,接著跳到函式宣告的地方,然後再按Ctrl + Shift + "下"方向鍵,然後便自動跳到函式內容地方。

星期一, 10月 29, 2007

複製程序(procedure)或函式(function)的程式碼後,在private能自動產生的方法

在程序名或函式名上(不用圈選)按右鍵,選擇complete class at cursor即可。

例外處理

Object Pascal定義了許多例外類別,在Delphi中,類別幾乎都以T開頭,但例外類別卻大部分是E開頭,例如:EDateTimeError, EConvertError,EDivByZero,ERangeError等,所有例外皆繼承自Exception 類別。

要在Delphi中在設計階段能使用 Exception的功能,須先設定下列步驟:

主選單 ==> Tools ==> Debugging Options...
==> Language Exceptions標籤頁中的 Stop on Delphi Exceptions 的勾勾取消即可。


try ... except ... 的範例:

try
保護的指令
except
有例外發生時,要如何處理
end;

為何要 try ... except ... ,其目的在於"保護的指令"該區段若有任何的錯誤,不希望造成系統的停止運作,而是將運作的流程直接跳到 except區段,由該區段來執行錯誤排除的工作。

範例一

procedure TForm1.Button1Click(Sender: TObject);
var
d: Tdatetime;
i,j: integer;
begin
try
d:= strtodatetime(edit1.Text);
i:= strtoint(edit2.text);
j:= strtoint(edit3.text);
i:= i div j;
edit2.Text:= inttostr(i);
except
on EDivByZero do showmessage('除以零的錯誤');
//可以自訂錯誤訊息
on Econverterror do showmessage('型態轉換錯誤');
on e:Exception do //超過一行的指令需用begin...end; //任何變數名稱都可以,不一定要e,在此為臨時的變數名稱。 Exception為所有例外處理的老祖宗
showmessage('系統訊息: ' + e.Message);
end;
end;


自製的 raise Exception型態,程式設計者可以自訂錯誤訊息,並將例外的情形集中處理

interface
type
EMyException=class(Exception); //自訂的錯誤型態

begin
try
i:= strtoint(edit1.Text);
j:= strtoint(edit2.Text);
if i < 10 then
raise EMyException.Create('edit1 值小於 10')
else if i > 100 then
raise EMyException.Create('edit1 值大於 100');
edit2.Text:= inttostr(i div j);
except
on e:EMyException do showmessage(e.Message);
//自訂的exception會自動移除,不須自己寫指令清除 e.destroy
on ee:Exception do showmessage('系統錯誤訊息:' + ee.Message);
end;
end;


Try ... Finally ... end;
目的為了保護系統的資源以便在完成工作後釋放原先所取得的資源,在Delphi中以下的資源通常需要下指令去取得,也需要下指令去釋放 Files,Memory,Windows resources (VCL only),Objects等。

格式如下

{ 取得資源的指令}
try
{ 利用該資源的相關指令}
finally
{ 最後須釋放相關資源}
end;


範例:
以下範例取得之記憶體的資源將無法正常的釋放,造成你在寫成程式,記憶體愈來愈少。

procedure TForm1.Button1Click(Sender: TComponent);
var
APointer: Pointer;
AnInteger, ADividend: Integer;
begin
ADividend := 0;
GetMem(APointer, 1024); // 要求 1K 的記憶體容量
AnInteger := 10 div ADividend; //若在此發生錯誤,則下列的釋放資源的工作將無法做到
FreeMem(APointer, 1024);
end;
}
//修改成下列方式,其意義就是不管 div的指令有無錯誤,皆須執行 finally內之指令,即釋放記憶體freemem。

procedure TForm1.Button1Click(Sender: TComponent);
var
APointer: Pointer;
AnInteger, ADividend: Integer;
begin
ADividend := 0;
GetMem(APointer, 1024); { allocate 1K of memory }
try
AnInteger := 10 div ADividend; { this generates an error }
finally
FreeMem(APointer, 1024); { execution resumes here, despite the error }
end;
end;


except 與 finally 合併使用
語法如下:

try
try
statement1;
statement2;
...
except
on E:except do ...//進行例外處理
end;
finally
statement_n; //釋放系統資源
end;

範例:

procedure TForm1.Button1Click(Sender: TObject);
var
i,j: integer;
begin
try
try
i:=strtoint(edit1.text);
j:=strtoint(edit2.text);
i:= i div j;
edit1.text:= inttostr(i);
except
on E:Exception do
showmessage(E.message);
end;
finally
showmessage('finally 這一段一定會做(釋放系統資源)');
end;
end;



Global exception handeling(一般不建議,因為太多的exception,會更亂)
如果希望將所有的 Exception 統一在副程式中處理,可以利用 Application.onException的屬性,將應用程式中所有的例外狀況,交由同一段指令來處理。

使用步驟如下:

//1. 宣告處理Exception的程序名稱,例如MyExceptionHandler
{ Public declarations }
procedure MyExceptionHandler(Sender : TObject; E : Exception );

//2. 定義 Exception Handler於 implementation區,例如:
procedure TForm1.MyExceptionHandler(Sender : TObject; E : Exception );
var
wRetVal : Word;
begin
wRetVal := MessageDlg('錯誤訊息: ' + E.Message, mtError, mbAbortRetryIgnore,0);
case wRetVal of
mrAbort:
begin
{ handle "Abort" here... }
end;
mrRetry:
begin
{ handle "Retry" here... }
end;
mrIgnore:
begin
{ handle "Ignore" here... }
end;
else
begin
{ handle "other" action here...}
end;
end;
end;

//3. 指定應用程式的Exception將集中至 MyExceptionHandler管理
procedure TForm1.FormCreate(Sender: TObject);
begin
{ begin new code }
Application.OnException:=MyExceptionHandler;
{ end new code }
end;


巢狀式(分層次)的例外處理

try //第一層的保護區塊起點
statement...;

try //第二層的保護區塊起點
statement...;
except //第二層的例外處理程式
statement...;
end;
...
except //第一層的例外處理
on e:Exception do ...
end;

物件 : 多型(三)

『多型性』基本上就是一種樣式多種呈現的方式,或者說同一件事情有兩種以上的解釋。

例如在C++中 I = J + K 其中的 "+" 可能會因 J 及 K 的形態不同而有許多種不同的意義。
例如 生物的覓食 Eat() 具有多種不同的定義。
例如 每個人對賺錢 make_money() 皆有不同的方法。
Polymorphism可以做到軟體元件的Plug and Play 。

現代的OOP compiler(如C++,Java,object pascal) 皆支援 virtual function以完成 polymorphism 的機制。

polymorphism 的方式可以用 overloading 或 overriding 來解釋。

所謂 overloading上一章已經提過,主要就是同一個副程式的名稱,呼叫是依據輸入引數的型態或輸入引數的數量來決定,執行哪一個副程式。

而 overriding則是與繼承性有關,任何父類別中的副程式,若宣告為 virtual或 dynamic則可以被子類別的副程式所覆蓋 (指用override的指令)。如果 父類別的副程式不想實現真正的程式碼,則須在加上宣告 abstract。

overriding只對 virtual 或 dynamic的副程式有效,所以任何要做 overriding皆須在宣告的後面加上 virtual 或 dynamic,不加的話是屬於 static 的副程式(一般 function 或 procedure 預設為 static),無法 overriding。

其中 virtual method可以有較快的執行速度,而 dynamic method可以有較小的程式碼,一般來說 virtual method將較為有效率。

polymorphism 更重要的是父類別的功能可以由子類別來取代,如此在執行階段,將會非常有彈性地,我們由下列程式範例來說明。

5個class類別TFigure、TCircle、TPolygon、TSquare及TTriangle。
TCircle及TPolygon繼承Tfigure。TSquare及TTriangle繼承TPolygon。

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TFigure = class
public
procedure draw;virtual;abstract; //若要做overridding則用virtual;否則用abstract
// 宣告 abstract所以不須實作TFigure.draw之程式碼
// virtual亦可使用 dynamic,一般來說virtual要較大的記憶體空間,但效率較好,dynamic則較小的記憶體空間,但效率較差
end;
type
TCircle = class(TFigure)
public
procedure draw;override;

end;
type
TPolygon = class(TFigure)
public
procedure draw;override;
procedure static_draw;
end;
type
TSquare = class(TPolygon)
public
procedure draw;override;
end;
type
TTriangle = class(TPolygon)
public
procedure draw;override;
procedure static_draw;

end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TCircle.draw;
begin
showmessage('TCircle');
end;
procedure TPolygon.draw;
begin
showmessage('TPolygon');
end;
procedure TSquare.draw;
begin
showmessage('TSquare');
end;
procedure TTriangle.draw;
begin
showmessage('TTriangle');
end;


procedure TPolygon.static_draw;

begin
showmessage('static function -- polygon draw');

end;
procedure TTriangle.static_draw;

begin
showmessage('static function -- triangle draw');

end;
procedure TForm1.Button1Click(Sender: TObject);
var F: array[0..3] of TFigure;
i: integer;
p1: Tpolygon;

begin
F[0]:=TCircle.Create;
F[1]:=TPolygon.Create;
F[2]:=TSquare.Create;
F[3]:=TTriangle.Create;
for i:=0 to 3 do
F[i].draw; // 所謂的一種樣式多種表現形式
for i:=0 to 3 do // 釋放物件之記憶體,或可用 F[i].destroy;
F[i].Free; // 但 Free會先判斷是否有有被建立物件

//測試static function 與 virtual function 或 dynamic function之差異
p1:= TTriangle.create;

p1.static_draw; // 雖然p1由TTriangle所建立,仍靜態連結到Tpolygon
Ttriangle(p1).static_draw; //須強制轉態方能執行Ttriangle中的static_draw函數
p1.draw; // 這個會執行Ttriangle的draw,由此可了解virtual(dynamic)與static之差異

// 多型的功能須藉由virtual或dynamic function方能達到
p1.free;
end;
end.


type
TFirstComponent = class(TCustomControl)
procedure Move; { static method}
//procedure Move; abstract; {不想定義實做寫}
procedure Flash; virtual; { virtual method}
procedure Beep; dynamic; {dynamic virtual method}
end;

TSecondComponet = class(TFirstComponent)
procedure Move; //可重新定義,但不用寫overrides
procedure Flash; override; {overrides inherited method}
procedure Beep; override; {overrides inherited method}
end;