如何在非Form的純Unit下自定button的click事件 |
答題得分者是:Coffee
|
P.D.
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
請問各位:
以下是我創建一個TForm, 內有一組 TLabel 及 TButton 其中 TButton 的 onClick 事件, 如果我這支function 是放在一個標準的 TForm 下, 這樣的寫法是ok的, 但如果我是在一個 Unit 單元下(因為不具有Form的架構), onClick 的指定編譯時發生錯誤, 請問要如何改, 才能通過編譯 程式碼:
[code delphi] procedure AppDialog(vMessage: string); var wLabel: TLabel; Button1: TButton; begin if Form_WaitMsg <> nil then FreeAndNil(Form_WaitMsg); Form_WaitApp := TForm.Create(nil); Form_WaitApp.Font.Charset:= CHINESEBIG5_CHARSET; Form_WaitApp.Font.Name := '細明體'; Form_WaitApp.Font.Size := 10; wLabel := TLabel.Create(Form_WaitApp); Button1 := TButton.Create(Form_WaitApp); Form_WaitApp.Color := clBtnFace; Form_WaitApp.BorderIcons := [biSystemMenu]; Form_WaitApp.Caption := '警告'; Form_WaitApp.Position := poOwnerFormCenter; wLabel.Parent := Form_WaitApp; wLabel.Alignment := taCenter; wLabel.Layout := tlCenter; wLabel.Color := clBtnFace; wLabel.Font.Name := '標楷體'; wLabel.Font.Size := 20; wLabel.Font.Color := clMaroon; wLabel.Caption := vMessage; wLabel.AutoSize := True; wLabel.Layout := tlCenter; wLabel.Alignment := taCenter; Form_WaitApp.AutoSize:= True; Button1.Parent := Form_WaitApp; Button1.Align := alBottom; Button1.Height := 50; Button1.ModalResult := mrOK; Button1.forbidden := myButtforbidden; <== 這裡編譯會出錯 Incompatible types: method pointer and regular procedure Button1.Caption := '確 定'; Button1.Show; Form_WaitApp.Show; end; procedure myButtforbidden(Sender: TObject); begin if .... end; [/code] |
Coffee
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:31 回覆:878 積分:561 註冊:2006-11-15 發送簡訊給我 |
Delphi的事件串接是以class的method pointer來達成。
而考慮到事件的處理,所以在VCL的架構上將它轉為一個特化型別:TNotifyEvent,說明的部份可以參考Delphi文件。 這個連結是google來的一個範例,請參考:http://hi.baidu.com/lzj1981/blog/item/515f0b8f3bf811e5f11f365e.html 有點久沒使用TNotifyEvent,恕沒辦法現在直接寫個例子給你參考。 P.S. 稍徵注意一下的話,其實你可以看到Delphi IDE在物件的成員清單中會顯示onclick : TNotifyEvent
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。 為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。 在引述到我的文時自然會儘量替各位想辦法,謝謝大家! |
老大仔
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:78 回覆:837 積分:1088 註冊:2006-07-06 發送簡訊給我 |
假如您那個單獨的unit中加個:
type TButtEvent = Object procedure myButtClick(Sender: TObject); end; 然後: procedure TButtEvent.myButtClick(Sender: TObject); begin // end; 這樣子不知道行不行的通?? ===================引 用 P.D. 文 章=================== 請問各位: 以下是我創建一個TForm, 內有一組 TLabel 及 TButton 其中 TButton 的 onClick 事件, 如果我這支function 是放在一個標準的 TForm 下, 這樣的寫法是ok的, 但如果我是在一個 Unit 單元下(因為不具有Form的架構), onClick 的指定編譯時發生錯誤, 請問要如何改, 才能通過編譯 程式碼:
[code delphi] procedure AppDialog(vMessage: string); var wLabel: TLabel; Button1: TButton; begin if Form_WaitMsg <> nil then FreeAndNil(Form_WaitMsg); Form_WaitApp := TForm.Create(nil); Form_WaitApp.Font.Charset:= CHINESEBIG5_CHARSET; Form_WaitApp.Font.Name := '細明體'; Form_WaitApp.Font.Size := 10; wLabel := TLabel.Create(Form_WaitApp); Button1 := TButton.Create(Form_WaitApp); Form_WaitApp.Color := clBtnFace; Form_WaitApp.BorderIcons := [biSystemMenu]; Form_WaitApp.Caption := '警告'; Form_WaitApp.Position := poOwnerFormCenter; wLabel.Parent := Form_WaitApp; wLabel.Alignment := taCenter; wLabel.Layout := tlCenter; wLabel.Color := clBtnFace; wLabel.Font.Name := '標楷體'; wLabel.Font.Size := 20; wLabel.Font.Color := clMaroon; wLabel.Caption := vMessage; wLabel.AutoSize := True; wLabel.Layout := tlCenter; wLabel.Alignment := taCenter; Form_WaitApp.AutoSize:= True; Button1.Parent := Form_WaitApp; Button1.Align := alBottom; Button1.Height := 50; Button1.ModalResult := mrOK; Button1.forbidden := myButtforbidden; <== 這裡編譯會出錯 Incompatible types: method pointer and regular procedure Button1.Caption := '確 定'; Button1.Show; Form_WaitApp.Show; end; procedure myButtforbidden(Sender: TObject); begin if .... end; [/code] |
Coffee
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:31 回覆:878 積分:561 註冊:2006-11-15 發送簡訊給我 |
查了一下想起來了,TNotifyEvent 其實就是method pointer的alias。
請參考小弟先前發過的回覆:http://delphi.ktop.com.tw/board.php?cid=30&fid=70&tid=88676 原則上是跟老大仔是一樣的。 但是請注意小弟在這邊的作法,其轉接method的instance僅為一個,也就是說並不是為每一個instance都產生一個method instance。 如果在要求performance或有thread safe考量時,請避免此用方法或將其改善。
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。 為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。 在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2010-10-08 18:18:43, 註解 無‧
|
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
我想老大仔的回覆應該是可行的,雖然我覺得 Object 換成 Class 會不會好一點? 我很久沒寫delphi了…忘了 object 的意義…它好像是object pascal裡(delphi前身) 的定義方法?
但pd可能還會想要知道,為什麼? 為什麼這樣寫可以? 因為 type TNotifyEvent = procedure( Sender: TObject) of object; 它必需是 方法指標,也就是一個類別裡的一個方法。不能是一個純的procedure! 所以自行隨意創一個類別後,再加入一個方法,然後再指給forbiden就可以成功的型別吻合了! ===================引 用 老大仔 文 章=================== 假如您那個單獨的unit中加個: type TButtEvent = Object procedure myButtClick(Sender: TObject); end; 然後: procedure TButtEvent.myButtClick(Sender: TObject); begin // end; 這樣子不知道行不行的通??
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
Coffee
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:31 回覆:878 積分:561 註冊:2006-11-15 發送簡訊給我 |
這樣是可行的沒錯,但是delphi似乎未提供在語言層級上的delegate機制(印象中C#好像是?),因此沒有辦法將Object換成class以讓caller在呼叫時才去建立該method的instance。
of object指的就是class method pointer這個type,而我的印象中沒有of class的用法。 而TNotifyEvent只針對type去定義,因此老大仔的回覆亦須如同我的方法一樣,先為這個額外的method所屬的class產生出一個instance,caller instance在串接的時候再將事件指定到這個object上。 Delphi印象中有Metaclass可以使用,但沒有去研究過。 但是目前討論的這些作法都會有thread safe的問題跟performance issue。 ===================引 用 aftcast 文 章=================== 我想老大仔的回覆應該是可行的,雖然我覺得 Object 換成 Class 會不會好一點? 我很久沒寫delphi了…忘了 object 的意義…它好像是object pascal裡(delphi前身) 的定義方法? 但pd可能還會想要知道,為什麼? 為什麼這樣寫可以? 因為 type TNotifyEvent = procedure( Sender: TObject) of object; 它必需是 方法指標,也就是一個類別裡的一個方法。不能是一個純的procedure! 所以自行隨意創一個類別後,再加入一個方法,然後再指給forbiden就可以成功的型別吻合了! ===================引 用 老大仔 文 章=================== 假如您那個單獨的unit中加個: type TButtEvent = Object procedure myButtClick(Sender: TObject); end; 然後: procedure TButtEvent.myButtClick(Sender: TObject); begin // end; 這樣子不知道行不行的通??
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。 為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。 在引述到我的文時自然會儘量替各位想辦法,謝謝大家! |
sryang
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:39 回覆:762 積分:920 註冊:2002-06-27 發送簡訊給我 |
[code delphi]
var AMethod: TMethod; ... AMethod.Code := @TButtEvent.myButtClick; AMethod.Data := Form_WaitApp; // 這個就是 method 執行時的 Self Button1.OnClick := TNotifyEvent(AMethod); [/code] 或是參考 這一篇
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/ |
P.D.
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
先感謝樓上所有前輩提供的資訊
我拜讀了coffee 兄的前作, 恕小弟功力有限, 基本上是無法看明白, 雖然原理大概知道, 但就是沒有辦法化為實作, 反而是同篇中的 kingron前輩提出的三種做法, 我一一嘗試, 最後兩種均可編譯成功, 同時我試run, 在第一次有成功, 但結束程式引發 Access Error Read/Write 的錯誤, 但也很可惜就那麼一次ok, 之後, 不管如何操作, 只要一執行 MsgDialog('xxxx') 就立即引發 Access Error( Can not write xxx read xxx) 的錯誤, 不管如何編譯是成功的, 但執行都是失敗的, 更慘的是, 我把剛才加入的相關 code 全部移除, 連 dcu 都刪除掉, 再重新編譯, 還是成功的, 可是一執行一樣發生 Access Error 的錯誤, 我現在不知道引發錯點在那裡, 因為所有的code 都移除了, 理應回到原始我所設計的點才對, 不應該有錯誤的, 不過我只要不執行 MsgDialog() 就沒事, 換句話, 整支程式就是已經死了, 直接我把舊的 pas 及 dcu 備份還原回來才再度復活, 唉! 真不知是我手拙還是Delphi討厭我? 今天看到 sryang 前輩的回帖, 我也照做, 同樣編譯是過了, 但執行一樣發生這個問題, 移除加上的code, 也是回不來!! 其實我想做的就是做一個MessageBox 取代原有的 Application.MessageBox, 至於我可以控制裡面顯示的字體, 排列順序, 顏色 的功能, 但又不想利用一個form來製作, 當然我知道有很多種方式可以做, 站上也有不少討論, 不過因為剛好採用這種做法出問題, 所以就提出來了! ===================引 用 sryang 文 章=================== [code delphi] var AMethod: TMethod; ... AMethod.Code := @TButtEvent.myButtClick; AMethod.Data := Form_WaitApp; ?? // 這個就是 method 執行時的 Self Button1.OnClick := TNotifyEvent(AMethod); [/code] 或是參考 這一篇 |
Coffee
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:31 回覆:878 積分:561 註冊:2006-11-15 發送簡訊給我 |
如果我沒猜錯,問題應該是這個
[code delphi] procedure TMyCustomForm.onCreate//或者是建構子 begin self.onMyMsgEvent:=CommonObj.MyCustomMsg;//減少在建構子或create event後建立,似乎Delphi跟MFC都一樣 end; procedure TMyCustomForm.onFormShow begin if not assigned(self.onMyMsgEven) then self.onMyMsgEvent:=CommonObj.MyCustomMsg;//在FormShow的時候去檢查是否已指向共用事件 end; destructor TMyCustomForm.onDestroy begin if assigned(self.onMyMsgEven) then self.onMyMsgEven:=nil; //在物件被釋放之前將此event設為null以避免共用的物件method被釋放 //在你執行第一次的時候可以是因為CommonObj.MyCustomMsg還存在,第一次執行完畢後被物件連同一起釋放,所以第二次執行同樣的事件時就會跳到被釋放掉的區塊。 inherited;//進行物件原本該有的釋放動作 end; [/code] 我自己寫的code類似TNotifyEvent,但我是對不同參數的method去定義再加以宣告。 如果可以還是用TNotifyEvent或TMethod應該會比較符合framework。 另外我第一個回覆有提到,event的部份一定要是類別中的方法,所以你宣告成一般的方法是一定不行的。 那個鍽譯錯誤就是告訴你兩個類型不合,一個是method(類別中的方法),一個是regular procedure。 ===================引 用 P.D. 文 章=================== 先感謝樓上所有前輩提供的資訊 我拜讀了coffee 兄的前作, 恕小弟功力有限, 基本上是無法看明白, 雖然原理大概知道, 但就是沒有辦法化為實作, 反而是同篇中的 kingron前輩提出的三種做法, 我一一嘗試, 最後兩種均可編譯成功, 同時我試run, 在第一次有成功, 但結束程式引發 Access Error Read/Write 的錯誤, 但也很可惜就那麼一次ok, 之後, 不管如何操作, 只要一執行 MsgDialog('xxxx') 就立即引發 Access Error( Can not write xxx read xxx) 的錯誤, 不管如何編譯是成功的, 但執行都是失敗的, 更慘的是, 我把剛才加入的相關 code 全部移除, 連 dcu 都刪除掉, 再重新編譯, 還是成功的, 可是一執行一樣發生 Access Error 的錯誤, 我現在不知道引發錯點在那裡, 因為所有的code 都移除了, 理應回到原始我所設計的點才對, 不應該有錯誤的, 不過我只要不執行 MsgDialog() 就沒事, 換句話, 整支程式就是已經死了, 直接我把舊的 pas 及 dcu 備份還原回來才再度復活, 唉! 真不知是我手拙還是Delphi討厭我? 今天看到 sryang 前輩的回帖, 我也照做, 同樣編譯是過了, 但執行一樣發生這個問題, 移除加上的code, 也是回不來!! 其實我想做的就是做一個MessageBox 取代原有的 Application.MessageBox, 至於我可以控制裡面顯示的字體, 排列順序, 顏色 的功能, 但又不想利用一個form來製作, 當然我知道有很多種方式可以做, 站上也有不少討論, 不過因為剛好採用這種做法出問題, 所以就提出來了! ===================引 用 sryang 文 章=================== [code delphi] var AMethod: TMethod; ... AMethod.Code := @TButtEvent.myButtClick; AMethod.Data := Form_WaitApp; ?? // 這個就是 method 執行時的 Self Button1.OnClick := TNotifyEvent(AMethod); [/code] 或是參考 這一篇
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。 為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。 在引述到我的文時自然會儘量替各位想辦法,謝謝大家! |
P.D.
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |