再次向高手提个问题,关于procedure of object变量赋值的问题。(150分)

  • 主题发起人 主题发起人 dirk
  • 开始时间 开始时间
D

dirk

Unregistered / Unconfirmed
GUEST, unregistred user!
我有个程序,要将TreeView的OnChange事件赋值为不同的处理过程,于是我在一个unit中
定义了
procedure TreeViewChange1(Sender: TObject
Node: TTreeNode);
procedure TreeViewChange2(Sender: TObject
Node: TTreeNode);
……
若干个处理过程,然后把这些TreeViewChange过程按不同情况赋值给TreeView的OnChange
事件,
TreeView1.OnChange := TreeViewChange1;
……
TreeView1.OnChange := TreeViewChange2;
但是却出错:
Incompatible type:method pointer and regular procedure
发现,只有对象中定义的过程才能赋值给对象过程,而普通的过程不能赋值给用of object
定义的过程,如 TTVChangedEvent = procedure(Sender: TObject
Node: TTreeNode) of object;
过程,怎么办?难道我要给事件过程赋值,就非得先定义一个类,在里面定义n个过程,再
实例化一个对象,然后再把这些过程赋值给其他对象吗?

我用 @TreeView1.OnChange := @TreeViewChange1
的方式赋值倒是不出错,但是在
TreeViewChange1过程中出错:
Access violation at address 0042786B in module 'Project1.exe'.Read of address 00110536
看样子也是不行的啦!

再次请教各位高手,有没有办法解决?我实在不想定义一个类,再实例化一个对象这样来为
对象事件过程赋值,谢谢啦!
 
当然不用你说的那么麻烦了,否则,Delphi还有人用嘛^_^?
你的全部代码我们看到,但是按照你的描述,开始就没错,你看我下面的代码,编译就不报错

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
TreeView1: TTreeView;
procedure Button1Click(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
procedure TreeViewXChange(Sender: TObject
Node: TTreeNode);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.TreeViewXChange(Sender: TObject
Node: TTreeNode);
begin
ShowMessage('Here');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.OnChange := TreeViewXChange;
end;

end.
 
唉,老大,当然不出错啦!
你难道没有注意到TForm1.TreeViewXChange前有个TForm1吗?你把 TreeViewXChange 定义
在TForm1外面试试看,编译能通过吗?
把你的代码改一改:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
TreeView1: TTreeView;
procedure Button1Click(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
end;
procedure TreeViewXChange(Sender: TObject
Node: TTreeNode);

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TreeViewXChange(Sender: TObject
Node: TTreeNode);
begin
ShowMessage('Here');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.OnChange := TreeViewXChange
// <--这样肯定出错
end;

end.
 
//不要忘了dummy: Integer;
procedure Test(dummy: Integer
Sender: TObject);
begin
ShowMessage('Hello, World!');
end;

procedure TForm1.FormCreate(Sender: TObject);
var
M : TMethod;
begin
M.Code := @Test;
Button1.OnClick := TNotifyEvent(M);
end;
 
太谢谢你了tseug,能否再给我解释一下,为什么非要用个dummy: Integer,好像改用其他
变量名也可以,而且,取它的值,总是0,不知有什么用?
 
方法被调用的时候有一个隐含参数, 也就是那个类的实例, 是一个指针. 我这样声明
保留了这个参数的位置, 返回时不至于破坏堆栈..
 
比较高深,不是很懂,在delphi的帮助上能找到这些信息吗?
 
这是一种非公开的方法,Help中好象找不到!
 
那你们怎么知道的呢?有什么地方兜售delphi编程中非公开的方法的地方吗?[:D][:D][:D]
 
并不用加个参数的:
procedure ButtonClick(Sender: TObject);
begin
if Sender is TButton then
ShowMessage(TButton(Sender).Caption);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
M: TMethod;
begin
M.Code := @ButtonClick;
M.Data := Button2;
Button2.OnClick := TNotifyEvent(M);
end;

dirk:
其实这些东西都有书说到,只不过可能你没看到,或者忽略了
如macro cantu的《Mastering Delphi6》的第156页就有这样的代码:
var
Method: TMethod;
Evt: TNotifyEvent;
begin
Method.Code := MethodAddress (‘Button1Click’);
Method.Data := Self;
Evt := TNotifyEvent(Method);
Evt (Sender)
// call the method
end;
 
唉,我书看得是少了点,主要在编程中学到东西。

但是,xianjun,你的代码会出错,如下:
procedure TreeViewChange1(Sender: TObject
Node: TTreeNode);
begin
ShowMessage('Node Text is :'+Node.Text);
end;

procedure TForm1.Button6Click(Sender: TObject);
var
M : TMethod;
begin
m.Code :=@TreeViewChange1;
m.Data :=Treeview1;
Treeview1.OnChange :=TTVChangedEvent(m);
end;

但是在OnChange事件中出错。
 
是执行到TreeViewChange1里时出错。
 
[:D][:D][:D]又偷学了不少东西
 
这个呢就要象tseug说的那样: “//不要忘了dummy: Integer;”
因为Delphi在调用方法指针的时候会把Self作为第一个参数传过去(在这里就是m.Data了)
然后再传递其他的参数,因此在我第一个Demo里
Sender参数其实传的就是M.Data即Button2了,这与Button.Onclick里的Sender就一样了
所以此时不加参数Dummy也能得到正确的结果
但是在
procedure TreeViewChange1(Sender: TObject
Node: TTreeNode);
begin
ShowMessage('Node Text is :'+Node.Text);
end;
这里就不行啦,此时Sender是M.Data,Node就是TTVChangedEvent里的Sender参数,所以就出错了。
故你要在前面加个冗余参数
procedure TreeViewChange1(Dummy: Integer
Sender: TObject
Node: TTreeNode);
传递过来的就是: Dummy = M.Data; Sender与Node就是TTVChangedEvent里相应的参数了。
 
to xianjun:
你说“Node就是TTVChangedEvent里的Sender参数”,但是我用
ShowMessage(sender.ClassName );看到返回的明明是TTreeView啊?
 
那你换成
procedure TForm1.Button6Click(Sender: TObject);
var
M : TMethod;
begin
m.Code :=@TreeViewChange1;
m.Data :=nil;
Treeview1.OnChange :=TTVChangedEvent(m);
end;
试试看ShowMessage(Sender.ClassName)是什么东西,肯定是非法地址访问错误
 
我又试了一下,xianjun说的没错啊!

发分了!
 
高手,佩服。
 
说得好...这个隐含的参数好象是self吧?
 
后退
顶部