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

如何在非Form的純Unit下自定button的click事件

答題得分者是:Coffee
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#1 引用回覆 回覆 發表時間:2010-10-08 17:22:52 IP:118.169.xxx.xxx 未訂閱
請問各位:

以下是我創建一個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]
編輯記錄
P.D. 重新編輯於 2010-10-08 17:23:46, 註解 無‧
P.D. 重新編輯於 2010-10-08 17:24:40, 註解 無‧
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2010-10-08 17:57:08 IP:220.130.xxx.xxx 訂閱
Delphi的事件串接是以class的method pointer來達成。
而考慮到事件的處理,所以在VCL的架構上將它轉為一個特化型別:TNotifyEvent,說明的部份可以參考Delphi文件。
這個連結是google來的一個範例,請參考:http://hi.baidu.com/lzj1981/blog/item/515f0b8f3bf811e5f11f365e.html
有點久沒使用TNotifyEvent,恕沒辦法現在直接寫個例子給你參考。

P.S. 稍徵注意一下的話,其實你可以看到Delphi IDE在物件的成員清單中會顯示onclick : TNotifyEvent

------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2010-10-08 17:59:38, 註解 無‧
Coffee 重新編輯於 2010-10-08 18:02:24, 註解 無‧
老大仔
尊榮會員


發表:78
回覆:837
積分:1088
註冊:2006-07-06

發送簡訊給我
#3 引用回覆 回覆 發表時間:2010-10-08 18:02:46 IP:59.120.xxx.xxx 未訂閱
假如您那個單獨的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

發送簡訊給我
#4 引用回覆 回覆 發表時間:2010-10-08 18:12:22 IP:220.130.xxx.xxx 訂閱
查了一下想起來了,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

發送簡訊給我
#5 引用回覆 回覆 發表時間:2010-10-08 18:39:15 IP:210.64.xxx.xxx 訂閱
我想老大仔的回覆應該是可行的,雖然我覺得 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

發送簡訊給我
#6 引用回覆 回覆 發表時間:2010-10-08 18:47:40 IP:220.130.xxx.xxx 訂閱
這樣是可行的沒錯,但是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;

這樣子不知道行不行的通??


------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2010-10-08 18:48:49, 註解 無‧
Coffee 重新編輯於 2010-10-08 18:49:48, 註解 無‧
Coffee 重新編輯於 2010-10-08 18:52:41, 註解 無‧
Coffee 重新編輯於 2010-10-08 19:00:43, 註解 無‧
sryang
尊榮會員


發表:39
回覆:762
積分:920
註冊:2002-06-27

發送簡訊給我
#7 引用回覆 回覆 發表時間:2010-10-11 15:21:50 IP:111.254.xxx.xxx 訂閱
[code delphi]
var AMethod: TMethod;
...
AMethod.Code := @TButtEvent.myButtClick;
AMethod.Data := Form_WaitApp; // 這個就是 method 執行時的 Self
Button1.OnClick := TNotifyEvent(AMethod);
[/code]

或是參考 這一篇
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/
編輯記錄
sryang 重新編輯於 2010-10-11 15:22:44, 註解 無‧
sryang 重新編輯於 2010-10-11 15:31:16, 註解 無‧
sryang 重新編輯於 2010-10-11 15:32:51, 註解 無‧
sryang 重新編輯於 2010-10-11 15:39:52, 註解 無‧
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#8 引用回覆 回覆 發表時間:2010-10-11 22:11:06 IP:118.169.xxx.xxx 未訂閱
先感謝樓上所有前輩提供的資訊
我拜讀了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

發送簡訊給我
#9 引用回覆 回覆 發表時間:2010-10-11 23:53:13 IP:114.43.xxx.xxx 訂閱
如果我沒猜錯,問題應該是這個
[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]

或是參考 這一篇
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2010-10-11 23:56:23, 註解 無‧
Coffee 重新編輯於 2010-10-11 23:58:36, 註解 無‧
Coffee 重新編輯於 2010-10-12 00:03:32, 註解 無‧
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#10 引用回覆 回覆 發表時間:2010-10-21 00:26:59 IP:118.160.xxx.xxx 未訂閱
抱歉, 最近因為案件趕交, 所以沒空上來, 然後最近ktop又是上一斷二的, 有關這個問題, 目前我使用 showmodal 的方式 可以解決(之前是想以 show 方式), 所以暫時沒有去測各位前輩的方案, 壓於我提到的按各位的方式加入的修正, 我想coffee 兄最後提到的應該是重點, 暫時先結案, 等有空再好好鑽研一下, 謝謝各位的協助!!
系統時間:2024-04-26 7:31:19
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!