線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:4610
推到 Plurk!
推到 Facebook!

Delphi 元件撰寫常問問題

 
jackkcg
站務副站長


發表:891
回覆:1050
積分:848
註冊:2002-03-23

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-10-21 19:34:38 IP:61.221.xxx.xxx 未訂閱
此為轉貼資料 Delphi 元件撰寫常問問題 -------------------------------------------------------------------------------- 第一部分 簡介 1.1 此份文件的目的?何? 這份文件的目的是?了解答有關撰寫 Delphi元件時常見或文件上找不到的問題。我曾經花了一段很長的時間來瞭解探索 TDataLink 類別,這讓我覺得應該將撰寫元件時常遇到的問題及經驗心得寫下來,分享給大家。不過我並不能保證寫在這份文件裏頭的解答完全正確。如果你對其中的任何問題有更好的解決方法,或認?有什?資訊適合放在這份文件裏的話,請告知作者。有任何錯誤或缺漏也歡迎指正。 除了再加上更多的問題及解答外,我試著再補充兩個部分: 進階程式設計師喜愛的工具:這也許跟元件設計沒有直接的關係但至少它們跟 Delphi有關係。 值得參考的文件刊物:由於空間的關係,這份文件不能放置太多的範例程式,因此參考其他文件是十分需要的。這不是一份教材式的文件,我不會做太多條理式的說明,但會試著將最具有參考價值的文獻列出。 如果你有任何意見或建"",歡迎來信告訴我。 -------------------------------------------------------------------------------- 第二部份 ""合環境 2.1 在""合環境中如何找出元件所?生的問題? 我發現唯一能找出問題的方法只有: 在 Delphi ""合環境的 Tools|Options 對話方塊的 Library 頁中將『Compile with debug info』選項打勾。 選 Component|Rebuild Library 重新編譯元件庫。 從 Turbo Debugger 中執行 Delphi。 選File|Change Dir移至包含元件程式碼的目錄下。 如果你的元件發生GPF時就可以檢視堆疊然後得知到底是哪些發生問題了。 2.2 如何檢視 Delphi 所?生的組合語言碼? Glen Boyd 的回答: 開?登錄編輯程式(REGEDIT.EXE),接著到『HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging』下新增一個字串機碼『EnableCPU』,將它的字串值設?『1』。此後Delphi""合環境的View選單下就會多一個『CPU』選項,它會開?一個視窗來檢視目前程式指令的記憶體及組合語言。你可以在偵錯時利用單步追蹤或其他方法來觀察它。 2.3 我可以在執行時期動態建立元件,但在設計時期就會發生錯誤。?什?? 你的元件必須繼承自TComponent類別或其衍生類別。 你的元件建構函式及滅構函式宣告必須看起來像這樣: constructor Create(AOwner: TComponent); override; destructor Destroy; override ; 所有在published區段宣告的欄位型態必須是ordinal、single、double、extended 、comp、currency、string、small set(譯注:指元素編號不超過0..31這個範圍的集合;平常的集合可容許的範圍?0..255)、method pointer或class其中一種。如果你宣告了其他型態的欄位,Delphi編譯器並不會檢查出錯誤。然而當你使用這個元件時依然會得到一個GPF。 如果你想讓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; 2.4 如何撰寫一個無法放置到表格上的元件? Ray Lischner 的回答: 如果你不想讓使用者將元件拉曳至表格上的話,使用 RegisterNoIcon 及 RegisterClass 程式來註冊元件。 2.5 在程式碼編輯器中快速切換程式區段最簡單的方法是什?? Ray Konopka 的回答: 在探索 VCL 原始程式碼時,強烈建""你最好熟悉程式碼編輯器裏的書簽功能。使用方法很簡單:Ctrl-Shift-N,N 是從 0 至 9 的數位,用來設定一個書簽。此後就可以使用 Ctrl-N 來跳躍至書簽處。(譯注:使用這項功能真的可以節省你許多來回卷動程式及找尋函式的時間,別遲疑了,快學吧!) 2.6 如何使我的元件在按下滑鼠右鍵時出現快速功能選單? 你必須要建立一個元件編輯器。元件編輯器決定了元件在設計時期時對滑鼠鍵的反應及動作,你可以?元件定義它自己的快速功能選單。 建立元件編輯器的步驟大致如下: 從 TComponentEditor 類別繼承一個新的類別。 改寫類別的 GetVerbCount、GetVerb及 ExecuteVerb方法。 在 Register 程式中使用 RegisterComponentEditor 程式來註冊此元件編輯器。 有關元件編輯器這個主題在『Developing Delphi Components』這本書中有詳盡的解說及資訊。 2.7 ?什?元件在設計時期會出現『I/O 103』的錯誤? 你可能在元件中使用了Writeln這個程式。 2.8 ?什?元件編輯器不會將元件屬性的變動儲存起來? 我發現有時自製的元件編輯器不會將元件屬性儲存起來。設計時期一切正常,但是儲存起來再重新讀入後就有問題了。 原因是你很可能忘了在元件編輯器中呼叫此方法: Designer.Modified; 如此一來Delphi才會知道你的元件編輯器更改過屬性值了。 -------------------------------------------------------------------------------- 第三部分在元件中使用其他元件 3.1 如何在元件中加入卷軸元件並讓它在設計時期能動作? 你的卷軸元件類別必須處理 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); 3.2 如何建立Windows95式樣的卷軸? 你必須設定卷軸的頁面大小。你可以用以下的程式碼來做: 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; -------------------------------------------------------------------------------- 第四部分 Bound Controls 4.1 哪里可以找得到有關 TDataLink 類別的說明文件? 我可以大膽地說全世界有關 TDataLink 的說明文件只有一份,就在這兒: 屬性 (Property) 介紹 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的資料記錄數目。 方法 (Method) 介紹 function Edit: Boolean; 讓所連結的DataSet進入編輯狀態。傳回值: 成功傳回 True ,失敗傳回 False procedure UpdateRecord; 我們不直接呼叫這個方法,它是提供其他程式來呼叫的。這個方法只有設定一個旗幟然後呼叫 UpdateData 方法。 虛擬方法 ( Virtual Method ) 要讓 TDataLink 物件與元件溝通必須改寫下列這些方法: procedure ActiveChanged 當連結的 DataSource 開?狀態改變時會呼叫此方法。使用 Active 屬性可以得知目前是否?開?狀態。 procedure CheckBrowseMode 資料庫有任何改變之後都會先呼叫這個方法。 procedure DataSetChanged; 當下列任一事件發生時都會呼叫此方法: 移至DataSet的開頭 移至DataSet的結尾 在DataSet中插入或新增資料 刪除DataSet的資料 取消DataSet的編輯 更新記錄 如果不想改寫這個方法只要在其中呼叫: 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 程式來防止資料庫更新。 4.2 如何得知一個 dataset 中有幾筆記錄? TDateSet 的 RecNo 屬性可以傳回資料記錄的數目,但很不幸地它只適用於 dBase 及 Paradox 的資料表格。若想得知目前資料記錄的編號,可以從 TDataLink 類別衍生一個新的類別,然後進行下 列步驟: 改寫 DataSetScrolled 方法以取得目前記錄是否被卷動。 改寫 DataSetChanged 方法來確認目前記錄是否跳至最前面或最後面了。 接著你可以將這個新類別的物件連結到TDataSource物件上然後就可以隨時得知目前的記錄編號了。 -------------------------------------------------------------------------------- 第五部分 VCL 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; 5.3 什?是元件訊息? 元件訊息是什??它十分類似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視窗訊息時會送給本身這個訊息。 5.4 我的元件得到輸入焦點後仍不能接受鍵盤訊息,?什?? 如果你的元件有 DragMode 屬性而且將它設成 dmAutomatic 時,很有可能讓你的元件以?它正被拖曳但實際上並沒有的情況。在 Controls 單元中有一個區域變數 DragControl 指 向目前正被拖曳的元件。你遇到的情況很可能就是明明沒有拖曳的動作但是DragControl 變數卻指向你的元件。在 TWinControl 的 WndProc 方法中,當 DragControl 變數指向元件本身時,會忽略所有鍵盤訊息,這就是原因了! -------------------------------------------------------------------------------- 第六部分 其他資訊 6.1 有哪些書介紹或講解如何撰寫元件? 有關撰寫元件的『標準』參考書籍: 『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 6.2 有哪些Web站臺可以取得撰寫元件的資訊? 全世界最大的 Delphi Web 站臺『Delphi SuperPage』 (譯注:亞洲地區使用者可以就近到位於日本的 Mirror Site ) 我在下面這些站臺中找到許多元件的原始程式碼: Temple of Delphi Delphi Free Stuff (譯注:『Your Delphi Programming Resource』""理元件也十分用心! ) (譯注:臺灣地區目前維持最好的 Delphi 站臺是『32 Bit Delphi 深度曆險』及其 Mirror Site) 你也可以使用一些搜尋引擎來尋找有關 Delphi 的站臺: Yahoo Alta Vista (譯注:Excite 搜尋引擎也別錯過羅!) -------------------------------------------------------------------------------- 第七部分 元件的儲存與載入 7.1 如何將包含其他物件的物件一起存入 DFM 檔? 我試過許多方法,包括改寫元件的 DefineProperties及 WriteComponents方法,但都還是失敗了。所以我只能說要解決這個問題的話只有使用 Delphi 原本的方法一途。 將包含其他物件的物件一起儲存起來的步驟大致如下: 確定你要儲存的所有物件都是從 TComponent 類別衍生下來的。 將所有需要儲存的變數宣告在 published 區段。 在元件的 Register 程式中呼叫 RegisterComponents程式來註冊所有你要儲存起來的類別。 每個擁有子控制項的類別必須改寫 GetChildren 方法以儲存每個子控制項。(在 Delphi 1.0 中你必須改寫 WriteComponents 方法並且?每個子控制項呼叫 WriteComponent方法)。 將物件載入的方法用了點小技巧。你必須改寫元件的 GetChildOwner 及 GetChildParent 方法,否則 Delphi會將所有物件的擁有者都設定?元件所在的表格。(在Delphi 1.0 中你必須改寫ReadState方法)。 7.2 如何得知元件是否正從資料流中讀出? 當元件正從資料流中讀出時,它的 ComponentState 屬性會包含csLoading 旗幟。 constructor TMyClass.Create(AOwner: TComponent); begin if csLoading in AOwner.ComponentState then begin ... end else begin ... end; end; 7.3 如何確定元件的屬性是否被正確地儲存? 有許多很簡單的方法可以驗證屬性是否被正確地儲存在檔案裏: 在""合環境中用滑鼠右鍵點選表格然後選擇『View as Text』。然而萬一 DFM 檔 有任何錯誤的話,你將什?也看不到。 開個 DOS 視窗,利用 Delphi 所附的『Convert』程式將 DFM 檔轉成文字格式。 Stefan Hoffmeister 指出複製或剪下元件後,到任何一個文書編輯器(如記事本)中貼上,你就可以看到此元件的文字表示。你甚至可以編輯這些文字表示後再將它貼回 Delphi""合環境的表格上。 -------------------------------------------------------------------------------- 第八部分 Delphi 的工具 8.1 有沒有Delphi版本的 YACC 及 LEX? 有。Albert Graef 這位仁兄寫了 Turbo Pascal 版本的 YACC 及 LEX,也可以讓 Delphi 使用。 你可以在 ftp://ftp.simtel.net/pub/simtelnet/msdos/turbopas 下取得 tply30a1.zip 及 tply30a2.zip 這兩個檔案,其中還包含這兩個工具的原始程式哦! 8.2 如何秀出 JPEG 格式圖形檔? Jacques Nomssi Nzali 將 Independent JPEG Group 所發展的免費 JPEG 程式庫改寫成 Pascal 版本。你可以從下取得: PASJPG10.ZIP Independent JPEG Group 的免費 JPEG 函式庫 rev 6a 之 Pascal 版本 (1.0 版)。 -------------------------------------------------------------------------------- 第九部分 基本程式設計技巧 9.1 如何建立不定數目的物件陣列? 最簡單的方法是使用 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; 9.2 Delphi 2.0的 WinCrt單元到哪去了? Delphi 2.0並沒有 WinCrt單元。 先別傷心,這是因?我們可以用其他方法來取代它。在 Project|Options 的 Linker 頁次中將『Generate console application』選項打開,你就可以像以前使用 WinCrt 單元一樣地寫程式了! 9.3 自製元件時該從哪個類別繼承? VCL 中有一些『自訂』類別,而且有許多控制項是直接由這些『自訂』類別繼承下來的。例如 TMemo 直接繼承自 TCustomMemo類別。這些自訂類別寫好了所有該控制項所擁有的功能,只是沒有將屬性公開出來而己。大部分情形下,你應該從那些自訂類別繼承而不是控制項類別。 如果你要從頭撰寫自己的元件,那?從 TCustomControl 類別繼承是個不錯的主意。撰 寫出來的元件會具有 Window Handle 且可以接受輸入焦點。 另外根據你的需要也可以從這些類別繼承: TGraphicControl:視覺元件,但是沒有window handle,也不能接受輸入焦點。 TComponent:不可視元件,你沒辦法在執行時期看到它。 TWinControl:將已存在的視窗元件包裝起來,如Windows標準控制項或VBX元件。 -------------------------------------------------------------------------------- 第十部分 進階程式設計技巧 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 類別那?具有彈性。 10.2 如何取得列舉型態變數的文字表示? 使用 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; 11.2 如何得知是否表格上所有元件都已載入完成? Loaded 方法是在載入完成後接著被呼叫的。 procedure TMyClass.Loaded; begin inherited Loaded; // 將ComponentState中的 csLoading 狀態清除 // 在這裏撰寫你想要執行的動作 end; 11.3 在哪里繪製元件最適合? 你應該攔截 WM_PAINT 視窗訊息然後利用 Canvas 來繪製元件。然而 VCL 己經幫你攔 截好了,你只須改寫元件的 Paint 方法即可。 procedure TMyClass.Paint; begin // 如果你的元件是己存在的元件繼承下來的,那?必須在這裏呼叫 inherited Paint inherited Paint ; // 在這裏撰寫你想要執行的動作 end; 11.4 如何改變元件的視窗式樣? 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; -------------------------------------------------------------------------------- 第十二部分 Windows API 12.1 元件卷動時閃動的很厲害,如何克服這種情況? 要卷動元件本身最簡單的方法就是改變它的座標然後重畫元件,但是這方法會導致元件閃動的很厲害。 比較好的方法是呼叫 ScrollWindow 或 ScrollWindowEx Windows API。 閃動的另一個原因可能來自於 WM_PAINT 及 WM_ERASEBKGND。你可以試著攔截 WM_ERASEBKGND 及 WM_PAINT 訊息然後自己處理繪圖動作,包括繪製背景的動作,或許可以改善閃動的情況。 12.2 如何重新?動Windows? 使用 ExitWindowsEx Windows API。 12.3 如何快速大量地更改元件資料? 在進行大量資料更改前後,利用 WM_SETREDRAW 訊息來控制你的元件暫時不要重畫,這不但可以使資料設定速度增快也防止元件閃爍的情況。 -------------------------------------------------------------------------------- 第十三部分 控制項邊框 13.1 ?什?我的元件的 Ctl3D 屬性設? True 之後,它依然沒有 3D 的邊框呢? 如果 ControlStyle 屬性內沒有包含 csFramed 旗幟那? Ctl3D 屬性就會沒有作用。在元件 的建構函式內加上: ControlStyle := ControlStyle [csFramed]; 13.2 如何實作 BorderStyle 屬性? 在控制項設定有沒有邊框之後要重新建立 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; -------------------------------------------------------------------------------- 第十四部分 控制項式樣 14.1 當元件重繪時如何防止閃動的情況? 如果元件的 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; -------------------------------------------------------------------------------- 第十五部分 視窗訊息 15.1 ?什?我的元件得不到方向鍵的訊息? 你必須攔截 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方法。
------
**********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好

Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind
系統時間:2024-11-23 5:08:42
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!