Wolfgang Chien's Homepage | Delphi學習筆記 - 典藏篇 |
版本:5
譯者: 陳寬達 原作者 :John M. Miano
Copyright 1996 - John Miano
協助: Glen Boyd Stefan Hoffmeister Ray Konopka Ray Lischner Max Nilson
最近修改日期:1997年1月1日 最近修改章節:6.1
譯稿更新日期 1997 年 3 月 3日
第一部分 簡介
第二部份 整合環境
第三部分 在元件中使用其它元件
第四部分 Bound Controls
第五部分 VCL
第六部分 其它資訊
第七部分 元件的儲存與載入
第八部分 工具程式
第九部分 基本程式設計技巧
第十部分 進階程式設計技巧
第十一部分 元件虛擬方法
第十二部分 Windows API
第十三部分 控制項邊框
第十四部分 控制項式樣
第十五部分 視窗訊息
這份文件的目的是為了解答有關撰寫 Delphi元件時常見或文件上找不到的問題。我曾 經花了一段很長的時間來了解探索 TDataLink 類別,這讓我覺得應該將撰寫元件時常 遇到的問題及經驗心得寫下來,分享給大家。不過我並不能保證寫在這份文件裡頭的 解答完全正確。如果你對其中的任何問題有更好的解決方法,或認為有什麼資訊適合放在這份文件裡 的話,請告知作者。有任何錯誤或缺漏也歡迎指正。
除了再加上更多的問題及解答外,我試著再補充兩個部分:
如果你有任何意見或建議,歡迎來信告訴我。
我發現唯一能找出問題的方法只有:
如果你的元件發生GPF時就可以檢視堆疊然後得知到底是哪些發生問題了。
Glen Boyd 的回答:
開啟登錄編輯程式(REGEDIT.EXE),接著到『HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging』下新增一個字串機 碼『EnableCPU』,將它的字串值設為『1』。此後Delphi整合環境的View選單下就會 多一個『CPU』選項,它會開啟一個視窗來檢視目前程式指令的記憶體及組合語言。你 可以在偵錯時利用單步追蹤或其它方法來觀察它。
2.3 我可以在執行時期動態建立元件,但在設計時期就會發生錯誤。為什麼?
constructor Create(AOwner: TComponent); override;
destructor Destroy; override ;
如果你想讓TMyComponent元件可以在設計時期操作,注意下面的宣告會引發十分嚴重 的問題:
type TComplex = record
RealPart: Double;
ComplexPart: Double;
end;
class TMyComponent = Class(TComponent)
private
F1: TComplex;
published
property P1: TComplex read F1 write F1;
end;
Ray Lischner 的回答:
如果你不想讓使用者將元件拉曳至表格上的話,使用 RegisterNoIcon 及 RegisterClass 程序來註冊元件。
2.5 在程式碼編輯器中快速切換程式區段最簡單的方法是什麼?
Ray Konopka 的回答:
在探索 VCL 原始程式碼時,強烈建議你最好熟悉程式碼編輯器裡的書籤功能。使用方法 很簡單:Ctrl-Shift-N,N 是從 0 至 9 的數字,用來設定一個書籤。此後就可以使用 Ctrl-N 來跳躍至書籤處。(譯註:使用這項功能真的可以節省你許多來回捲動程式及 找尋函式的時間,別遲疑了,快學吧!)
你必須要建立一個元件編輯器。元件編輯器決定了元件在設計時期時對滑鼠鍵的反應及動作,你可以為元件定義它自己的快速功能選單。
建立元件編輯器的步驟大致如下:
有關元件編輯器這個主題在『Developing Delphi Components』這本書中有詳盡的解 說及資訊。
2.7 為什麼元件在設計時期會出現『I/O 103』的錯誤?
你可能在元件中使用了Writeln這個程序。
我發現有時自製的元件編輯器不會將元件屬性儲存起來。設計時期一切正常,但是儲 存起來再重新讀入後就有問題了。
原因是你很可能忘了在元件編輯器中呼叫此方法:
Designer.Modified;
如此一來Delphi才會知道你的元件編輯器更改過屬性值了。
你的捲軸元件類別必須處理 CM_DESIGNHITTEST 元件訊息才行。
TMyScrollBar = class (TScrollBar)
procedure CMDesignHitTest
(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
end;
procedure TMyScrollBar.CMDesignHitTest( var Message: TCMDesignHitTest);
begin
Message.Result := 1;
end;
你的元件必須以以下方法建立捲軸:
TMyScrollBar.Create(nil);
而不是
TMyScrollBar.Create(Self);
你必須設定捲軸的頁面大小。你可以用以下的程式碼來做:
procedure SetPageSize(ScrollBar: TScrollBar; PageSize: Integer);
var
ScrollInfo: TScrollInfo;
begin
ScrollInfo.cbSize := Sizeof (ScrollInfo);
ScrollInfo.fMask := SIF_PAGE;
ScrollInfo.nPage := PageSize;
SetScrollInfo (ScrollBar.Handle, SB_CTL, ScrollInfo, True);
end;
要取得目前頁面大小可用如下方法:
function GetpageSize (ScrollBar: TScrollBar): Integer;
var
ScrollInfo: TScrollInfo;
begin
if HandleAllocated then
begin
ScrollInfo.cbSize := SizeOf (ScrollInfo);
ScrollInfo.fMask := SIF_PAGE;
GetScrollInfo (ScrollBar.Handle, SB_CTL, ScrollInfo);
Result := ScrollInfo.nPage;
end;
end;
4.1 哪裡可以找得到有關 TDataLink 類別的說明文件?
我可以大膽地說全世界有關 TDataLink 的說明文件只有一份,就在這兒:
property Active: Boolean(唯讀)
當此 DataLink 連結至一個已開啟的 DataSource 時會傳回 True。當 Active 狀態改變時會 觸發ActiveChanged方法。
property ActiveRecord: Integer(可讀寫)
用來設定或取得 DataLink 緩衝區中目前所指向的記錄代碼,代碼的範圍是 0 .. BufferCount - 1。使用它來設定記錄代碼時必須小心不要超過這個範圍,否則 可能導致不可預期的錯誤。
property BufferCount: Integer(可讀寫)
DataLink 擁有一個資料緩衝區。而 BufferCount 屬性即用來設定或取得緩衝區大小, 緩衝區大小決定了一個dataset同時可以顯視的資料記錄筆數。對大部分的資料感知元 件來說,BufferCount 的值是 1;但對 TDataGrid 來說,BufferCount 代表它的可 視列數目。
property DataSet: TDataSet(唯讀)
傳回此 DataLink 所連結的 DataSet。其實就是 DataSource.DataSet。
property DataSource: TDataSource(可讀寫)
傳回此DataLink所連結的DataSource。
property DataSourceFixed: Boolean(可讀寫)
這個屬性可用來防止 DataSource 屬性被更改。如果此屬性設為 True,當我們試著改變 DataSource 屬性時會引發一個例外。
property Editing: Boolean(唯讀)
如果 DataLink 正處於編輯狀態則傳回 True。
property ReadOnly: Boolean(可讀寫)
設定 DataLink 是否為唯讀狀態。這個屬性並不會影響所連結的 DataSet。在唯讀狀態下這個 DataLink 無法進入編輯狀態。
property RecordCount: Integer(唯讀)
傳回DataSet的資料記錄數目。
function Edit: Boolean;
讓所連結的DataSet進入編輯狀態。傳回值: 成功傳回 True ,失敗傳回 False
procedure UpdateRecord;
我們不直接呼叫這個方法,它是提供其它程式來呼叫的。這個方法只有設定一個旗幟 然後呼叫 UpdateData 方法。
要讓 TDataLink 物件與元件溝通必須改寫下列這些方法:
procedure ActiveChanged
當連結的 DataSource 開啟狀態改變時會呼叫此方法。使用 Active 屬性可以得知目前是否為開啟狀態。
procedure CheckBrowseMode
資料庫有任何改變之後都會先呼叫這個方法。
procedure DataSetChanged;
當下列任一事件發生時都會呼叫此方法:
如果不想改寫這個方法只要在其中呼叫:
RecordChanged(nil);
procedure DataSetScrolled(Distance: Integer)
每當目前記錄變更時會呼叫此方法。Distance 參數代表緩衝區欲捲動的行數。 (其值範圍皆在 -1 .. 1 之間)。使用 ActiveRecord 屬性可以取得緩衝區中目前所指向的記錄。我們無法強制讓 DataLink 的緩衝區捲動。
procedure FocusControl(Field: TFieldRef)
與TField.FocusControl方法相同。
procedure EditingChanged
當 DataLink 的編輯狀態改變時會呼叫此方法。使用 Editing 屬性可以得知DataLink 是否 正處於編輯狀態。
procedure LayoutChanged
當 DataSet 的 Layout 改變時會呼叫此方法(例如新增一個column)。
procedure RecordChanged(Field: TField)
當下列任一事件發生時都會呼叫此方法:
procedure UpdateData
在一筆記錄被更新以前會呼叫此方法。你可以呼叫 Abort 程序來防止資料庫更新。
TDateSet 的 RecNo 屬性可以傳回資料記錄的數目,但很不幸地它只適用於 dBase 及 Paradox 的資料表格。若想得知目前資料記錄的編號,可以從 TDataLink 類別衍生一個新的類別,然後進行下 列步驟:
接著你可以將這個新類別的物件連結到TDataSource物件上然後就可以隨時得知目前的 記錄編號了。
5.1 使用整合環境除錯時如何追蹤檢視 VCL元件的程式碼?
將你想要追蹤的 VCL 原始程式單元拷貝至存放專案的目錄中並重新編譯元件庫,此後你就可以在那些 VCL單元中追蹤檢視程式碼了。
5.2 我的元件參考到其它元件,如何得到參考元件被消滅的訊息?
Max Nilson 的回答:
TComponent 類別提供了 Notification 方法。當一個元件被移除時我們可以利用這個方 法得到消息以進行適當的反應。你可以參考『Component Writer's Guide』內有關 Notification 及FreeNotification 這兩個方法的說明。
當你的元件參考到另一個元件,例如,你的元件中有一個 TDataSource 型態的屬性。那 你必須改寫此元件的 Notification 方法,在其中檢查被移除的元件是否就是本身所參 考的元件。預設情況下,當元件被移除時,所有其它在同一個表格上的元件才會收到 消息,如果參考元件位於另一個表格上時,你的元件無法得知這件事情。Delphi 2.0 推出了TDataModule,參考元件位於另一個表格上的機會大幅增加,所以你應該利用 FreeNotification 方法來確定當參考元件移除時,你一定可以得到消息。
如果你不改寫 Notification 方法來處理參考元件被移除的訊息,這會讓 Delphi整合環境陷入十分不穩定的狀態。它可能不會立刻當掉,但你也不能再正常地繼續其它工作 了。
下面是一個範例,當你的元件參考其它元件時,千萬記得要做以下的處理:
TMyComponent = class (TComponent)
private
FDataSource: TDataSource;
procedure SetDataSource(Value: TDataSource);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
published
property DataSource: TDataSource read FDataSource write SetDataSource;
end;
procedure TMyComponent.SetDataSource(Value: TDataSource);
begin
if Value <> FDataSource then
begin
FDataSource := Value;
// 告訴參考元件說,當它被移除時記得通知我一聲。
if FDataSource <> nil then FDataSource.FreeNotification(Self)
end;
end;
procedure TMyComponent.Notification(AComponent: TComponent; Operation:TOperation);
begin
inherited Notification(AComponent, Operation);
// 如果被移除的正是參考元件,把FDataSource欄位清除。
if (Operation = opRemove) and (AComponent = FDataSource) then
FDataSource := nil
end;
元件訊息是什麼?它十分類似Windows的視窗訊息,只有一點不同:元件訊息只適用於 VCL 元件;而視窗訊息可以用在系統內所有具有 window handle 的控制項或視窗。如果 你有一個具有 Font 屬性的元件(例如TLabel元件),當我們更改它的 Font 屬性時並沒 有送出視窗訊息(譯註:TLabel 元件不是視窗控制項,根本也沒有視窗 Handle可以讓 我們傳送視窗訊息),但是控制項仍然知道字型改變了所以要重畫自己,為什麼?因 為我們有元件訊息。
元件訊息不可以由虛擬方法來處理,這可能是設計 VCL 時的考量,大概是因為不想讓虛 擬方法表格(Virtual Method Table)過於龐大的原因。
『Secrets of Delphi 2.0』這本書對於所有的元件訊息有十分詳盡的解說。
接下來我們列出一些比較常見的元件訊息及它們的作用。 標示著『Notification Only』 的訊息表示送出這個訊息只是為了通知元件某件消息而己,並不傳入任何參數而且也 不需要傳回值。
CM_ACTIVATE (Notification Only)
當表格成為焦點視窗時會傳給本身這個訊息。
CM_CTL3DCHANGED (Notification Only)
當控制項的Ctl3D屬性更改時會傳給本身這個訊息。
CM_DESIGNHITTEST 參數:TCMDesignHitTest 傳回值:0或1
在設計時期當滑鼠移到元件上頭時,整合環境會送給此元件這個訊息。此訊息的目的 用來決定元件在設計時期是否要處理滑鼠訊息。如果傳回值是 1,整合環境就讓元件 自行處理滑鼠訊息;若傳回值是 0,則整合環境會幫你處理滑鼠訊息。如果傳回值永 遠是 1,那麼元件的快速功能選單則永遠不會出現;如果元件不處理這個訊息或永遠 傳回 0,那此元件在設計時期將無法對滑鼠訊息做任何反應。
CM_FONTCHANGED (Notification Only)
控制項的字型改變後送給本身此訊息。
CM_FONTCHANGE (Notification Only)
當控制項收到WM_FONTCHANGE視窗訊息時會送給本身這個訊息。
CM_PARENTCTL3DCHANGED (Notification Only)
當元件父控制項的Ctl3D屬性改變或設定新的父控制項時會收到此訊息。
CM_PARENTCOLORCHANGED (Notification Only)
當元件父控制項的 Color 屬性改變或設定新的父控制項時會收到此訊息。
CM_PARENTFONTCHANGED (Notification Only)
當元件父控制項的Font屬性改變或設定新的父控制項時會收到此訊息。
CM_PARENTSHOWHINTCHANGED (Notification Only)
當元件父控制項的ShowHint屬性改變或設定新的父控制項時會收到此訊息。
CM_WININICHANGE 參數:TWMWinIniChange 傳回值:無
當控制項收到WM_WININICHANGE視窗訊息時會送給本身這個訊息。
如果你的元件有 DragMode 屬性而且將它設成 dmAutomatic 時,很有可能讓你的元件以為它正被拖曳但實際上並沒有的情況。在 Controls 單元中有一個區域變數 DragControl 指 向目前正被拖曳的元件。你遇到的情況很可能就是明明沒有拖曳的動作但是DragControl 變數卻指向你的元件。在 TWinControl 的 WndProc 方法中,當 DragControl 變數指向元件 本身時,會忽略所有鍵盤訊息,這就是原因了!
有關撰寫元件的『標準』參考書籍:
『Developing Delphi Components』 作者:Ray Konopka 出版:Coriolis Group
下面這本書並不專注於元件寫作,但裡面提到許多元件撰寫者不可不知的資訊:
『Secrets of Delphi 2』 作者:Ray Lischner 出版:Waite Group
另外一本元件撰寫的好書,它有許多在『Developing Delphi Components』裡找不到的資訊:
『Programming Delphi Custom Components』 作者:Fred Bulback 出版:M&T Books
全世界最大的 Delphi Web 站台『Delphi SuperPage』
(譯註:亞洲地區使用者可以就近到位於日本的 Mirror Site )
我在下面這些站台中找到許多元件的原始程式碼:
你也可以使用一些搜尋引擎來尋找有關 Delphi 的站台:
我試過許多方法,包括改寫元件的 DefineProperties及 WriteComponents方法,但都還是失敗了。所以我只能說要解決這個問題的話只有使用 Delphi 原本的方法一途。
將包含其它物件的物件一起儲存起來的步驟大致如下:
將物件載入的方法用了點小技巧。你必須改寫元件的 GetChildOwner 及 GetChildParent 方法,否則 Delphi會將所有物件的擁有者都設定為元件所在的表格。(在Delphi 1.0 中你必須改寫ReadState方法)。
當元件正從資料流中讀出時,它的 ComponentState 屬性會包含csLoading 旗幟。
constructor TMyClass.Create(AOwner: TComponent);
begin
if csLoading in AOwner.ComponentState then
begin ... end
else
begin ... end;
end;
有許多很簡單的方法可以驗證屬性是否被正確地儲存在檔案裡:
Stefan Hoffmeister 指出複製或剪下元件後,到任何一個文書編輯器(如記事本)中 貼上,你就可以看到此元件的文字表示。你甚至可以編輯這些文字表示後再將它貼回 Delphi整合環境的表格上。
有。Albert Graef 這位仁兄寫了 Turbo Pascal 版本的 YACC 及 LEX,也可以讓 Delphi 使用。
你可以在 ftp://ftp.simtel.net/pub/simtelnet/msdos/turbopas 下取得 tply30a1.zip 及 tply30a2.zip 這兩個檔案,其中還包含這兩個工具的原始程式哦!
Jacques Nomssi Nzali 將 Independent JPEG Group 所發展的免費 JPEG 程式庫改寫成 Pascal 版本。你可以從下取得:
Independent JPEG Group 的免費 JPEG 函式庫 rev 6a 之 Pascal 版本 (1.0 版)。
最簡單的方法是使用 TList 類別。我發現從 TList 衍生一個新類別很有用處。接下來的程式碼示範如何為一個特定型態撰寫一個特別的 TList 類別,並且加進基本的錯誤檢查。
TListOfMyObject = class (TList)
private
function GetItems(Index: Ordinal): TMyObject;
public
property Items[Index: Ordinal]: TMyObject read GetItems;
procedure Add(AObject: TMyObject);
end;
function TListOfMyObject.GetItems (Index: Ordinal): TMyObject;
begin
if Index >= Count then
raise Exception.CreateFmt('Index(%d) outside range 1..%d', [Index, Count-1]);
Result := inherited Items[Index];
end;
procedure TListOfMyObject.Add (AObject: TmyObject);
begin
inherited Add(AObject);
end;
Delphi 2.0並沒有 WinCrt單元。 先別傷心,這是因為我們可以用其它方法來取代它。在 Project|Options 的 Linker 頁次中將『Generate console application』選項打開,你就可以像以前使用 WinCrt 單元一樣地寫程式了!
VCL 中有一些『自訂』類別,而且有許多控制項是直接由這些『自訂』類別繼承下來 的。例如 TMemo 直接繼承自 TCustomMemo類別。這些自訂類別寫好了所有該控制項所擁 有的功能,只是沒有將屬性公開出來而己。大部分情形下,你應該從那些自訂類別繼 承而不是控制項類別。
如果你要從頭撰寫自己的元件,那麼從 TCustomControl 類別繼承是個不錯的主意。撰 寫出來的元件會具有 Window Handle 且可以接受輸入焦點。
另外根據你的需要也可以從這些類別繼承:
10.1 Delphi 有與 C++ 一樣的 I/O Stream 類別嗎?
答案可以說有也可以說沒有。Delphi允許你建立自己的『文字檔驅動程式』,它可以 讓你使用Delphi 標準的 I/O 函式庫來處理非標準的 I/O,如處理 UNIX 格式的文字檔或處 理 Socket 所取得的資料。雖然沒有像 C++ 的 I/O Stream 類別那麼強大但應該也足夠一般用途使用了。
建立『文字檔驅動程式』的方法在『Object Pascal Language Guide』中有明述。此 外你也可以參考 VCL 的 Printer 單元。
Delphi有 TStream 類別,不過是設計用來將物件寫入資料流的,不像 C++ 的 I/O Stream 類別那麼具有彈性。
使用 TypInfo單元中的 GetEnumName 函式:
type
TMyType = (Value1, Value2);
var
TypeValue: TMyType;
begin
Writeln (GetEnumName(TypeInfo(TMyType), Ord(TypeValue));
end;
TypInfo單元中還有許多與型別資訊有關的函式。
『Secrets of Delphi 2.0』這本書有許多關於TypInfo單元的資訊,值得參考。
11.1 如何得知元件的window handle是何時建立的?
控制項的 window handle 是在 CreateWnd 方法中建立的。如果你想要在建立 window handle 後接著做某些動作那麼你應該改寫 CreateWnd 方法:
procedure TMyClass.CreateWnd;
begin
// 現在還沒取得 window handle
inherited CreateWnd;
// 呼叫 inherited 以取得 window handle
// 在這裡撰寫你想要執行的動作
end;
Loaded 方法是在載入完成後接著被呼叫的。
procedure TMyClass.Loaded;
begin
inherited Loaded;
// 將ComponentState中的 csLoading 狀態清除
// 在這裡撰寫你想要執行的動作
end;
你應該攔截 WM_PAINT 視窗訊息然後利用 Canvas 來繪製元件。然而 VCL 己經幫你攔 截好了,你只須改寫元件的 Paint 方法即可。
procedure TMyClass.Paint;
begin
// 如果你的元件是己存在的元件繼承下來的,那麼必須在這裡呼叫 inherited Paint
inherited Paint ;
// 在這裡撰寫你想要執行的動作
end;
CreateParams方法用來設定元件的視窗式樣及其它必須傳遞至 CreateWindowEx API 的 參數。要改變元件的視窗式樣,例如增加或拿掉元件的垂直捲軸只要改寫 CreateParams 方法:
procedure TMyControl.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
if IWantAScrollBar then
Params.Style := Params.Style or WS_VSCROLL
else
Params.Style := Params.Style and not WS_VSCROLL;
end;
要捲動元件本身最簡單的方法就是改變它的座標然後重畫元件,但是這方法會導致元件閃動的很厲害。
比較好的方法是呼叫 ScrollWindow 或 ScrollWindowEx Windows API。
閃動的另一個原因可能來自於 WM_PAINT 及 WM_ERASEBKGND。你可以試著攔截 WM_ERASEBKGND 及 WM_PAINT 訊息然後自己處理繪圖動作,包括繪製背景的動作,或 許可以改善閃動的情況。
使用 ExitWindowsEx Windows API。
在進行大量資料更改前後,利用 WM_SETREDRAW 訊息來控制你的元件暫時不要重畫, 這不但可以使資料設定速度增快也防止元件閃爍的情況。
13.1 為什麼我的元件的 Ctl3D 屬性設為 True 之後,它依然沒有 3D 的邊框呢?
如果 ControlStyle 屬性內沒有包含 csFramed 旗幟那麼 Ctl3D 屬性就會沒有作用。在元件 的建構函式內加上:
ControlStyle := ControlStyle + [csFramed];
在控制項設定有沒有邊框之後要重新建立 window handle:
FBorderStyle: TBorderStyle;
procedure SetBorderStyle(Style: TBorderStyle);
property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle;
procedure CreateParams(var Params: TCreateParams); override;
procedure TMyControl.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
if FBorderStyle = bsSingle then
Params.Style := Params.Style or WS_BORDER
else
Params.Style := Params.Style and not WS_BORDER;
end;
procedure TMyControl.SetBorderStyle(Style: TBorderStyle);
begin
if Style <> FBorderStyle then
begin
FBorderStyle := Style;
// 重新建立window handle
RecreateWnd;
end;
end;
如果元件的 ComponentStyle 屬性沒有包含 csOpaque 旗幟的話,呼叫 Invalidate方法時 會導致元件的背景先被擦掉再重繪。如果你在 Paint 方法中繪製背景,那你應該在元件的建構函式中加上:
ComponentStyle := ComponentStyle + [csOpaque];
Max Nilson的回答:
引起閃動另一個原因可能是 WM_ERASEBKGND 訊息的處理。當 VCL 控制項收到一個 WM_ERASEBKGND 訊息時,它會將元件的背景擦掉然後設定成預設的顏色。如果你的元 件衍生自 TWinControl,而且元件的顏色與背景顏色不同(例如圖形),每次重畫以 前都會將元件先清成背景顏色再重繪,這就是造成閃動的原因了!
解決的方法不難,你必須告訴 Windows 你要自行解決『所有的』繪圖動作。不過有一 個前提是,你一定要確定你的 Paint 方法將整個元件都畫過,如果你漏了什麼地方忘 了畫,那個部分的資料會由亂數組成,你能想見這情況嗎?使用這個方法可以加速你 的元件繪製動作(稍微快一點點),因為少了一個填滿背景顏色的動作。
type
TMyComponent = class (TWinControl)
...
protected
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
...
end;
procedure TBMyComponent.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
// 不要重繪背景,這會造成元件閃動
Message.Result := 0
end;
你必須攔截 WM_GETDLGCODE 才能處理方向鍵的訊息,在 WM_GETDLGCODE 的訊息處理 者中傳回 DLGC_WANTARROWS。如果你不這樣做,那方向鍵的功用就只能用來移動視窗 焦點而己。
Max Nilson 的回答:
想要你的元件能夠處理方向鍵,你必須要攔截 CM_WANTSPECIALKEY 元件訊息。 CM_WANTSPECIALKEY 元件訊息提供你比攔截 WM_GETDLGCODE 視窗訊息更容易且靈活的 判斷方法來決定是否需要某些特殊鍵的訊息。當控制項收到任何一個特殊鍵時就會送 出CM_WANTSPECIALKEY 元件訊息給控制項。
特殊鍵包括:VK_TAB、VK_LEFT、VK_RIGHT、VK_UP、VK_DOWN、VK_RETURN、VK_EXECUTE 、VK_ESCAPE 及 VK_CANCEL。如果訊息傳回值是非零值,這個鍵就會被送至 KeyPress 方法以供處理,否則這個鍵的訊息會被送至元件的父控制項,以預設方式來處理。
一個簡單的範例:
type
TMyComponent = class (TWinControl)
...
protected
procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
...
end;
procedure TMyComponent.CMWantSpecialKey(var Message: TCMWantSpecialKey); begin
inherited;
// 我們只想處理向左方向鍵,其它的特殊鍵都給 Windows 處理
if Message.CharCode = VK_LEFT then
Message.Result := 1;
end;
CM_WANTSPECIALKEY 元件訊息比 WM_GETDLGCODE 訊息更具有彈性的地方在這兒。我們 甚至可以根據是按下的是哪個特殊鍵才決定是否處理這個鍵。例如,你的控制項有三張影像,你可以讓使用者利用左右方向鍵來回檢視它們,如果翻到最後一張影像再按 向右鍵時,焦點就讓它離開元件,剩下的全部都讓 Delphi 來處理。
15.2 有沒有與 Visual Basic『DoEvents』同樣功能的函式?
有。Application.ProcessMessages方法。
首頁 | 學習筆記 | 主題公園 | 軟體下載 | 關於本站 | 討論信群 | 相約下次 |