绝对没有办法把普通的procedure赋给一个类实例的事件指针吗? 解决的话,再加300分。(50分)

  • 主题发起人 主题发起人 shangshang
  • 开始时间 开始时间
类型一样可以传递
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
Frightclick:TnotifyEvent;
procedure xxx(Sender: TObject);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}
procedure TForm1.xxx(Sender: TObject);
begin
showmessage('q3w2e3');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Frightclick:=xxx;
form1.OnClick:=Frightclick;
end;
 
szf:
呵呵~~,好像是您没看清题目吧?
“。。。把普通的procedure赋给。。。”,你说的是普通的procedure吗?

shangshang:
》》这种方式跟我的初中相差很大。。。
是不是我要先猜一猜您问题还隐含了什么条件,然后再回答?再把您的问题看一遍,您说我的回答有问题吗?
 
再看看各位有何高见先~~~
另,to savenight:
我说的就是最类似普通的事件句柄过程的
procedure myproc(Sender: TObject);
而TNotify 的定义是 procedure (Sender: TObject) of object;
结合题目的要求:
>>想给一个动态创建的按钮赋onclick,
>>我非常想类似这样
>>onclick:=Gproc;
总不成OnClick不是一个 TNotify的过程,而是您所说的
TDoIt=procedure(arg1:integer;arg2:string);
这样的自定义的类型吧。
 
szf:
我不知道您对“普通过程”的概念是从哪里来的。
过程或函数(function or procedure )的定义分为两种情况:
一是:普通过程和函数:Stand alone, or explicitly defined procedures and functions。
二是:方法:Object method, procedures and functions。
您说是方法不是过程。
不知我的理解对否?
 
to savenight
我是这么认为的:
procedure myProc(Sender: TObject); //这是一个普通过程和函数--即是您说的一

TMyClass=class
procedure myProc(Sender: TObject); //这是一个对象方法--即是你说的二
end;
楼主的要求是,把"一"所描述的普通过程当作"二"来用。
不知这样理解是否正确?
 
怎么问题回答到最后感觉变成语文补习啦?
 
先弄清楚需求,再解决问题,这是程序员的基本要求嘛~~~
 
适合“大富翁”吗?
查一下贴子吧。 这里回答问题主要是抢时间!
不管题目问的是什么, 先胡乱回答一堆代码。
到结束时分分时别人总不好意思给你0分吧。 或者来一句“同意xxxx的意见”。 到时一样有分。
 
(个人观点,接受批评:))
总的来说yygw的第三个方法还是值得借鉴的,起码他提醒了大家RTTI的重要性,不过还有些不足:
1.
》》procedure Gproc(Self: TObject; Sender: TObject);
第一个参数Self: TObject;完全是多余。
2.
》》 SetMethodProp(Button1, 'OnClick', M); // 调用 RTTI 过程,动态设置事件
若是换成这样恐怕就不好解决了吧 SetMethodProp(Button1, 'OnMouseDown', M);
3.
》》 M.Data := nil; // 指向类实例(该值对应着 Gproc 调用时的 Self 参数值,可为任意值,这里传nil)
错,Data 是很关键的,尤其是对OnCreate,OnMouseDown之类的事件。

4.
》》var
》》 M: TMethod; // 方法记录
》》begin
》》 M.Code := @Gproc; // 指向方法(这里是全局过程)地址
》》 M.Data := nil; // 指向类实例(该值对应着 Gproc 调用时的 Self 参数值,可为任意值,这里传nil)
》》 SetMethodProp(Button1, 'OnClick', M);
其实这里用TNotifyEvent代替TMethod也行,而且不用typinfo单元,当然下面的方法和yygw没有本质区别。
var
myevent: TNotifyEvent;
tmethod(myevent).Code :=@myproc;
tmethod(myevent).Data :=button2; //button2是很关键的,是隐含的sender
Button2.OnClick :=myevent;

+++++++++++++++++++++++++++
我的解决办法是:(存在的问题是在响应button3的OnMouseDown后出现AV,谁搞定了就贴初来吧,我去看球赛了)
var
myevent: TNotifyEvent;
myMouseEvent:TMouseEvent;

procedure myProc(sender:tobject);
begin
(sender as tbutton).Caption :='savenight';
end;

procedure myMouseDownProc(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
showmessage(inttostr(x)+','+inttostr(y));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
tmethod(myevent).Code :=@myproc;
tmethod(myevent).Data :=button2; //button2是很关键的,是隐含的sender
Button2.OnClick :=myevent;

tmethod(myMouseEvent).Code :=@myMouseDownProc;
tmethod(myMouseEvent).Data :=button3; //button3是很关键的,是隐含的sender
Button3.OnMouseDown :=myMouseEvent;
end;
+++++++++++++++++++++++++++

》》不管题目问的是什么, 先胡乱回答一堆代码。
不知道是不是在说我,先躲起来看球赛去吧。
 
to savenight:
1.第一个参数Self: TObject;
绝对不是多余的,虽然你不用它编译也能通过,还可能执行正确,但如果你想在过程中
调用参数的话,没有它可不行。用OnMouseDown这样的事件,如果不加第一个参数,就
是你上面那个错误了:)

Delphi默认使用寄存器加堆栈的方式传递过程参数,三个参数以内使用EAX、EDX、ECX
的顺序保存参数,三个以后用堆栈。对类的方法来说,EAX恒定是类实例指针Self,这样
Sender参数实际上放在EDX中做为第二个参数传递,如下面的代码:
procedure MyMouseDown(Self: TObject; Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ShowMessage(Format('Self: %d, Sender: %d, X: %d, Y: %d', [Integer(Self),
Integer(Sender), X, Y]));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
M: TMethod;
begin
M.Code := @MyMouseDown;
M.Data := nil;
OnMouseDown := TMouseEvent(M);
end;

上面的代码:
Self --> EAX
Sender --> EDX
Button --> CL // TMouseButton枚举类型其实内部作Byte用
其它三个放堆栈。如果把Self: TObject去掉,则MyMouseDown认为只有最后两个参数放在堆
栈,返回时POP EIP得到的是错误的指针,你将看到一个非常访问内存错。

2.用TMouseEvent一样可以,见前,不过要注意过程的参数必须配套。

3.M.Data := nil;
我指的是语法上、运行时可为任何值而没有错误。
M.Data保存了方法所对应的对象指针,用于在调用该方法时提供Self这个隐藏参数,在普
通过程中其实是没有用的。难道你还想在过程中用Self来访问一个按钮或窗体吗?
M.Data并不是Sender,Sender是由方法(这里是过程)调用方指定的。
例如VCL源码:
procedure TControl.DblClick;
begin
if Assigned(FOnDblClick) then FOnDblClick(Self);
end;
你修改M.Data来作为Sender其实是没用的。

4.引用TypInfo单元是因为调用SetMethodProp,如果你直接给事件赋值是不需要的。
定义M: TMethod只需要赋值时做一次强制类型转换,而你的定义需要两次,何苦呢?
Delphi的类型强制转换要求类型相容或长度一致,TMethod与事件指针是相容的
(8字节),事实上VCL在调用事件时,也是按TMethod的结构来调用的,而TNofityEvent
等类型在本质上与TMethod结构相同,但在Delphi这种强类型语言中使用它们可以避免很
多错误。

补充:
[red]Delphi其实是一种非常严谨的语言,用上面的方法并不符合Delphi的程序设计思想,仅作
为技巧而已,贪多易入魔道。作为研究可以,不提倡多用,也没有太多意义!![/red]

如果对控件开发有兴趣,请关注一下:
http://cnpack.yeah.net
 
First,sorry for typing lots of English which maybe obscure. (My IME is wrong)

>>1.第一个参数Self: TObject;...
Here,you are almost right, but fact, EAX holds the address of the parent object's data block.
Thanks for your reply ,Now I have a clear concept on Self:)

>>定义M: TMethod只需要赋值时做一次强制类型转换,而你的定义需要两次,何苦呢?
I said no material difference between our codes ,but style. Actually,you can also write it like this:
var
M: TMethod; begin
M.Code := @myProc;
M.Data := button2;
Button2.OnClick :=TNotifyEvent(m);

>>http://cnpack.yeah.net
Any conditions? If you think I have the ability,I want to try :).
 
很不错的讨论啊. 不过有没有大侠把它整理一下? 这样就更明晰了一点. ...我想大家一定会感激的.
 
非常感谢大家,近日上网时间极其不充裕,请包涵,向所有参与富翁致谢,并希望有不同观点的
大侠继续补充。

http://www.delphibbs.com/delphibbs/dispq.asp?lid=1161211 ,这里有300分奉上。
 
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure myproc(Sender: TObject);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.myproc(Sender: TObject);
begin
showmessage('hi');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Button1.OnClick:=myproc;
end;
 
补充一点最好使用TActionlist来做这类事情
 
netbirdfly,你回答的没有看明白标题哦。
 
多人接受答案了。
 
后退
顶部