提出问题:
Form1.Button1Click(form1);
Form1.Button1.Click;
1)这两种调用有什么区别?
2)Procedure Button1Click(Sender: TObject);
这个过程归属于TForm1,还是Button1?
相关原理:
1]{$R *.dfm}是什么?有什么用?
我们从Delphi 的$R编译指示符入手(这一点很重要):
The $R directive specifies the name of a resource file to be included in an application or library. The named file must be a Windows resource file and the default extension for filenames is .res. To specify a file name that includes a space, surround the file name with single quotation marks: {$R 'My file'}
,是指
1)通过$R编译指示符,从外部调入Windows资源文件;
我们这里研究的是由点击Delphi菜单 File->Application,默认生成的空白工程做为试验工程:
{=======================Unit1.pas====一开始自动生成的====================}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
end.
{===================================================================
通过
The * symbol has a special meaning in $R directives: it stands for the base name (without extension) of the source-code file where the directive occurs. Usually, an application'
,我们可以得出:
2)窗口 (.dfm or xfm) 类型文件也可看作一种Windows资源文件,通过{$R *.dfm}导入,它是由Delphi自定义的特殊资源文件,。
为什么要强调这个*符号呢?因为奥秘全隐藏在这个*里。
因为在日常一般计算机操作里,*除了代表乘号外,在Word等文字编辑软件中作为任意个字符的通佩符,就是这种语议,误导了我们:
以为{$R *.dfm}是导入该工程目录下所有的 .dfm。
这样理解是错误的,大错特错的。
在Delphi里,且针对试验工程里,{$R *.dfm} 仅能替换为另外一种形式:{$R Unit1.dfm},即文件Unit1.dfm,如下:
{==================Unit1.dfm==========一开始自动生成的====================}
object Form1: TForm1
Left = 192
Top = 125
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
end
{===================================================================
显而易见,这个*.dfm/Unit1.dfm窗口资源文件里,就是对Form1这个对象的最基本属性进行设定。
好了,这些重要的原理已经被挖掘出来了,但是你会问,这跟我们开头的问题有何关系?
不急,这是药引,是铺垫基础。
经过这样说明,我们把Unit1.pas替换为
{==========Unit1.pas======{$R *.dfm} 改为{$R Unit1.dfm}===========}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R Unit1.dfm}
end.
{===================================================================
F9一下,正常编译运行,和没修改一样:-)
2]解开button1.OnClick迷团
2.1]和往常一样,从组件Standard面板点击Button图标,再在Form1窗体上点一下,一个新的Button就诞生了:Button1。
好的,现在我们来看Delphi在上面过程中为我们做了什么:
{=======Unit1.pas=============================加入Button1=========}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R Unit1.dfm}
end.
{===================================================================
在Unit1.pas只是加上这么一句Button1: TButton;表明Button1已经加到TForm1类里了。
注意:
如果你在加Button1之前,TForm1类里已经有这么一句Button1: TButton;那么Delphi就会抱怨:
Error:A field or method named Button1 already exists.
你必须手动删除TForm1类底下Button1: TButton; 这可能是你的代码是直接从网上拷进去,而不是你自己一点点敲出来。
好了,我们再来看Unit1.dfm
{==================Unit1.dfm==========加入Button1====================}
object Form1: TForm1
Left = 461
Top = 69
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 176
Top = 139
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
end
end
{===================================================================
还是那么直白,这个Unit1.dfm窗口资源文件里,就是在已有Form1这个对象下,加入了新诞生的Button1最基本属性设定。
2.2] 对了,接下就是新建Button1.OnClick了:
1)在上Button1双击
2)选取Button1,在Object Inspector里选择Events选项卡,点击并且选择OnClick选项,再在右栏空白处双击
两者没什么不同,就是个人习惯不同。
Delphi 会帮我们写好:
procedure Button1Click(Sender: TObject);
procedure TForm1.Button1Click(Sender: TObject);
begin
|//键盘光标在这
end
{=======Unit1.pas=======1)加入Button1=======2)新建Button1.OnClick=====}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R Unit1.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
end.
{===========================================================
真的只有这些么?让我来看解答全篇的问题的答案:来看看罪魁祸首Unit1.dfm:
{==================Unit1.dfm=========1)加入Button1=====2)新建Button1.OnClick========}
object Form1: TForm1
Left = 461
Top = 69
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 176
Top = 139
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click//就是这里
end
end
{============================================================
Delphi又偷偷地瞒着我们做了什么了,它只是在Button1最基本属性后,附加了这么一句:
OnClick = Button1Click
这句的作用是什么呢?
既然它是一个赋值,我们来看看赋值号左右各是什么:
Button1.OnClick->FOnClick: TNotifyEvent= procedure(Sender: TObject) of object;//method pointer
也就是说Button1.OnClick是一种以(Sender: TObject)为参数的通知过程类型的属性,可被赋为TNotifyEvent类的过程
而Button1Click呢?
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
说明Button1Click是Form1对象的一个以(Sender: TObject)为参数的过程,这正是一种TNotifyEvent类的过程.
这样的赋值跟
var
i:integer;
i:=0;
Interger,String等Predefined预定义类型的赋值一样,不过这是面向对象里的赋值。
Button1.OnClick := Form1.Button1Click;
它把 Form1.Button1Click对应内存里的地址拷给Button1.OnClick,使得Form1和Button1都可调用Button1Click过程。如图所示:。
其实我们可以把在Unit1.dfm里删除OnClick = Button1Click这一行,其后在Form1.Create里加入
Button1.OnClick := Form1.Button1Click;
效果是一样的。
不过既然Delphi有这么一个机制,帮我们做好了,我们就不必每创建一个On事件就还得往.dfm里对应的对象里加入这一行,毕竟,Delphi就是以其RAD著称滴。
这就是为什么我们在接口部份删除
procedure Button1Click(Sender: TObject);
时会弹出这样的询问:
procedure TButton.Click;//第一步
var
Form: TCustomForm;
begin
Form := GetParentForm(Self);
if Form <> nil then Form.ModalResult := ModalResult;
inherited Click;//第二步
end;
procedure TControl.Click;
begin
{ Call OnClick if assigned and not equal to associated action's OnExecute.
If associated action's OnExecute assigned then call it, otherwise, call
OnClick. }
if Assigned(FOnClick) and (Action <> nil) and (@FOnClick <> @Action.OnExecute) then
FOnClick(Self)
else if not (csDesigning in ComponentState) and (ActionLink <> nil) then
ActionLink.Execute(Self)
else if Assigned(FOnClick) then
FOnClick(Self);//第三步
end
回答问题:
Form1.Button1Click(form1);
Form1.Button1.Click;
1)这两种调用有什么区别?
从结果来看,没有差异,就像新建Button1.OnClick了:
1)在上Button1双击
2)选取Button1,在Object Inspector里选择Events选项卡,点击并且选择OnClick选项,再在右栏空白处双击
从执行过程来看
1]Form1.Button1Click(form1);
//是直接调用Button1Click过程
2]Form1.Button1.Click;
TButton.Click>>TControl.Click>>OnClick>>Button1Click
从执行效率来看
Form1.Button1Click(form1)
优于
Form1.Button1.Click(form1)
2)Procedure Button1Click(Sender: TObject); 这个过程归属于Form1,还是Button1?
归属于Form1,跟button1对象是否存在没有关系,Button1只是把OnClick属性关联到Button1Click。
引出有益结论:
直接结论
就是方法(procedure/function)作为一种类型,也可被赋给对象的某个属性。
附录1:
{
The Delphi language supports separately compiled modules of code called units. Using units promotes structured, reusable code across projects. The most common units created in projects are form units, which contain the event handlers and other code for the forms used in projects. But units don't have to have forms associated with them. You can create and save a unit as a stand-alone file that any project can use. For example, you can write your own procedures, functions, DLLs, and components,
and put their source code in a separate unit file that has no associated form.
If you open and save a default new project, the project directory initially contains one unit source-code file (unit1.pas) and its associated form file (unit1.dfm or unit1.xfm).
Caution: Do not add more than one form into a single unit file. The associated form file (.dfm or .xfm) can only describe a single form.
When you compile or run the project or perform a syntax check on the project, the Delphi compiler produces an intermediate output file on disk from each unit's source code. By default the compiled version of each unit is stored in a separate
binary-format file with the same name as the unit file, but with the extension .dcu (Delphi compiled unit). You should never need to open these binary files, and you do not need to distribute them with the completed project. The compiled-unit format is specific to the compiler, and enables rapid compiling and linking.
Note: As an option, you can choose to have the compiler generate object files (with the extension .obj) for greater compatibility with other compilers, but this greatly reduces the speed of compiling and linking your project. It should have no effect on the quality of the final generated code, however.
The $R compiler directive links the TForm's binary form file. This adds the form file(s) in your project to the compiled executable.
Caution: Do not remove the {$R *.dfm} or {$R *.xfm} directive from a form unit file. Doing so will result in code that will never work correctly.