1、如何创建另一个控件
我需要我的控件在设计时创建后生成一个新的TPanel控件,该控件在设计状态下可以单独使用(就像TPageControl选择New Page时会多出一个TTabSheet一样)
2、如何响应父控件属性的改变
如何在父控件属性改变时做出相应的改变(如使Caption属性与父控件的同步)
请指教
我需要我的控件在设计时创建后生成一个新的TPanel控件,该控件在设计状态下可以单独使用(就像TPageControl选择New Page时会多出一个TTabSheet一样)
2、如何响应父控件属性的改变
如何在父控件属性改变时做出相应的改变(如使Caption属性与父控件的同步)
请指教
解决方案 »
- 如何在c和delphi之间传递二维数组?具体是C传给del写的dl一个string的字符串(指针),delphi格式化运算后返回二维数组给C的主程序?
- 软件的同步自动升级问题
- 错误 : "多步操作产生错误,请检查每一步的状态值"
- 怎么样才可以动态创建一个组件,比如:TButton组件
- 如何得到控件在屏幕上的坐标??
- 求助,关于activeform的问题
- 请问一下,我用DELPHI 7编写了WEB SERVICE,但我不知道该怎么用,请教我一下
- delphi7程序如何更新word2003目录内容
- 关于搜索文件(包含文字)。
- 怎样获得IE中选中文本??我想了好久了!还是没结果!(急!)
- ClientDataset为什么无法更新数据?
- 如果不知道注册表中二进制数据的长度怎么读取期全部内容?
2.VCL中定义了一些Parent消息,你可以在控件中处理这些消息。
TWinControl--TCustomControl---TCustomGrid
---TButtonControl--TCustomGroupBox
---TScrollingWinControl--TCustomPanel
---TCustomComboBox
---TCustomEdit
---TCustomListBox TControl的子类型用于非窗口式控件,TWinControl的子类型则用于窗口式控件。除非特殊需要,一般不直接从TControl和TWinControl派生新控件,而是从其子类型派生。这样可以充分利用原有的属性、事件和方法,减少很多工作量。 在这些构件类型中,非通用的属性、事件和方法都声明为protected。这样可以禁止构件用户访问,又能被子类型继承和修改。在新构件中,可以简单地把继承来的属性和事件重新声明为published,使构件用户能在设计期通过对象编辑窗口访问,也可以进而修改属性的默认值和读写方式,或是重载(override)事件处理子过程和其他构件方法,以修改其中的程序代码。重声明可以放宽访问权限,但不能 相反,例如,不可能把published属性重声明为private或protected。 为了增加新功能,常常需要定义全新的属性、事件和方法。定义时,一般总是把对用户开放的属性和事件声明为published,把方法声明为public或protected。 构件属性 在构件中,属性和方法往往可以相互替代。对构件用户来说,属性比方法更直观简便。因此,只要可能,应尽量以属性取代方法。属性类型包括简单类型(numeric,character,string)、枚举类型、集合类型、对象类型(例如font)和数组类型(例如TStrings 类型中的Strings)。其定义方法如下: type
private
FLayers:Integer;{内部存储用的变量}
functionGetLayers:Integer;{用来读属性值的方法}
procedureSetLayers(ALayers:Integer);{用来写属性值的方法}
published
propertyLayers:
IntegerreadGetLayerswriteSetLayersdefault1;
end; 每个属性都需要相应的private变量用于内部存储。按照约定,变量名以F打头,后跟属性名(此处为Layers),读写方法名称分别为Get加属性名和Set加属性名。写方法总是带一个与属性类型相同的参数,用以传送属性值。此参数可以传值,也可以传递变量。如果不定义写方法(省略write部分),此属性便成为只读属性。读写方法应该在private部分声明,以使其对构件用户和构件的派生对象保持隐蔽。读写方法除了取值和赋值之外,还可以附加其他操作代码,使属性读写产生附加效应。这正是属性可以取代方法的原因。如果不需要附加效应,可以不定义读写方法,采用直接访问格式来声明属性: propertyLayers:
IntegerreadFLayerswriteFLayersdefault1; default命令符用来指定属性的默认值,同时需要在构件的构造函数中为属性设置初值。default命令的作用是在窗体文件存盘时提供参照:若属性当前值与default命令指定的值不同,则把当前值保存在文件中,否则便无需保存。如果省略default命令,属性当前值总是保存在窗体文件中。 事件与事件处理过程 创建构件时,事件也被当做属性来处理,区别仅在于事件必须定义为过程类型,使其成为一个隐蔽指针,指向某个潜在的过程。当构件用户为事件指定处理子程序后,事件便成为指向该子程序的指针。事件的定义方式如下: type
private
FOnClick:TNotifyEvent;{声明事件变量以保存过程指针}
published
propertyOnClick:
TNotifyEventreadFOnClickwriteFOnClick;
end;
此例正是Delphi标准控件中Click事件的定义方式。可以看出,除了OnClick被定义为过程类型外,其定义格式与一般属性的直接访问格式几乎完全相同。Delphi预定义了所有标准事件的过程类型及标准事件所引发的虚方法。其中,Click事件将引发如下虚方法: procedureTControl.Click;
begin
ifAssigned(OnClick)thenOnClick(Self);
{以下是默认处理部分}
end; 其中,Assigned函数检验OnClick是否已分配了事件处理过程。如果返回值为True,则调用用户指定的事件处理过程。通过重载此虚方法,可以修改Click事件的处理方式。在重载的方法中,一般应先调用用户处理程序,然后再安排后续处理。在本例中,首行代码应当是inheritedClick。 需要注意的是,构件用户不一定会给事件指定处理程序,因此事件不能定义为函数类型,否则可能会指向返回值类型不定的空函数。如果需要事件处理过程返回某个值,可以借助var参数。调用用户程序之前应确保此参数包含有效返回值,以免用户未指定事件处理过程时出错。 如果Delphi标准事件不能满足需要,也可以自己定义事件。其核心思想是选择适当的Windows消息来引发构件中的事件过程。篇幅所限,不拟详述,请读者参阅有关资料。 方法处理要点
方法处理在创建构件时和使用构件时没有多大区别,但有些问题仍需要注意。首先要注意的是,构件通常是在事件处理过程中调用,而构件作者又无法预测用户将在什么环境下如何调用构件。因此,构件中的方法应尽量避免占用系统资源,避免使Windows停止对用户操作的反应。 创建构件时应随时意识到,此构件不仅可以直接调用,而且可用来创建别的构件。即使是对用户隐蔽的方法也应具有完整的功能和清晰的接口。除了属性读写方法之外,内部方法一般应声明为protected虚方法,以便被派生对象继承和重载。属性读写方法则应采用private声明严密保护。派生对象如果需要读写父对象的属性值,应该访问属性本身,没有必要直接访问其读写方法。 构件测试 制作构件的核心工作是编写构件单元,包括根据构件功能要求设定对用户开放的属性、事件和方法,设定用以实现这些部件的变量、过程和函数等等。除了属性和事件有 特殊格式之外,构件单元的设计方式与一般Delphi单元没有什么不同,只是单元中不能包含窗体。 在编写构件单元的过程中,可以借助一个测试窗体直接对其测试。以可视化方法在窗体上安排构件,本质上不过是自动生成调用构件的代码。即使构件未并入VCL库,无法使用可视化操作,也可以手工编写这些调用代码。这样测试,可以免去反复修改而导致的反复安装。 测试时,需先建立一个窗体单元,然后进行以下操作:
1.把被测构件单元名称加入窗体单元的uses语句中,并在public部分声明被测构件的对象实例。
2.在窗体单元的FormCreate子程序中调用被测构件的Create方法,以构造构件实例,其Owner参数设置为Self,即窗体本身。然后给Parent属性赋值,并适当设置其他属性值。Parent是容纳构件的父对象,如果是窗体本身,应设置为Self。
3.运行包含测试窗体的工程,找出构件程序中的错误。 注册构件 注册构件用的程序代码可以放在构件单元中,但在Delphi下注册构件时要求提供包含注册代码的源程序文件(.PAS文件),因此,比较好的方式是把构件核心代码编译成.DCU文件或.DLL动态链接库,在注册源文件中只放注册代码和外围程序。下面是注册代码实例:
type
TMyPanelΚclass(TCustomPanel)
TMyLabelΚclass(TCustomLabel)
procedureRegister;
implementation
procedureRegister;
begin
begin
if FStyle3D <> value then
begin
FStyle3D := value;
invalidate; //表示控件将重画
end;
end;
另外对于带阴影的文字还要定义阴影的偏移量ShadeXOffSet、ShadeYOffSet: property ShadowXOffSet: integer read FXOffSet write SetFXOffSet default 5; property ShadowYOffSet: integer read FYOffSet write SetFYOffSet default -5; 写方法SetFXOffSet()、SetFYOffSet()和上面SetStyle3D()类似,略。 要重画控件一般要重载Paint方法,此处只是重画文字,只须重载DoDrawText方法(TLabel的DoDrawText的功能就是绘制文字)。 DoDrawText的声明放在Protected中: procedure DoDrawText(var Rect: TRect; Flags: Longint); override; 此处DoDrawText()根据四种类型(正常、凸起、凹进、阴影)分别画出不同的文字: procedure TLeiLabel.DoDrawText(var Rect: TRect; Flags: Longint);
var
Text: string;
TmpRect : TRect;
begin
Text := GetLabelText;
if (Flags and DT_CALCRECT <> 0) and ((Text = '') or ShowAccelChar and
(Text[1] = '&') and (Text[2] = #0)) then Text := Text + ' ';
if not ShowAccelChar then Flags := Flags or DT_NOPREFIX;
Flags := DrawTextBiDiModeFlags(Flags);
Canvas.Font := Font; Case FStyle3D of
Raised:
begin
TmpRect := Rect;
OffsetRect( TmpRect, 1, 1 );
Canvas.Font.Color := clGray;
DrawText(Canvas.Handle, PChar(Text), Length(Text), TmpRect, Flags); TmpRect := Rect;
OffsetRect( TmpRect, -1, -1 );
Canvas.Font.Color := clWhite;
DrawText(Canvas.Handle, PChar(Text), Length(Text), TmpRect, Flags);
end ;
Lowered:
begin
TmpRect := Rect;
OffsetRect( TmpRect, 1, 1 );
Canvas.Font.Color := clWhite;
DrawText(Canvas.Handle, PChar(Text), Length(Text), TmpRect, Flags); TmpRect := Rect;
OffsetRect( TmpRect, -1, -1 );
Canvas.Font.Color := clGray;
DrawText(Canvas.Handle, PChar(Text), Length(Text), TmpRect, Flags);
end ;
Shadowed:
begin
TmpRect := Rect;
OffsetRect( TmpRect, FXOffSet, FYOffSet );
Canvas.Font.Color := clGray;
DrawText(Canvas.Handle, PChar(Text), Length(Text), TmpRect, Flags);
end;
end; if not Enabled then
Canvas.Font.Color := clGrayText
else
Canvas.Font.Color := Font.Color; DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags);
end;
二、使文字具有超链接属性 定义一个属性URL表示要链接的网址或Email地址。 Property URL : String read FURL write SetURL; 写方法SetURL如下: procedure TLeiLabel.SetURL(Value: String);
Begin
if FURL <> Value then FURL := Value ;
if FURL <> '' then Cursor := crHandPoint ;
end; 当点击此Label时要打开浏览器或收发邮件工具,这便要重载Click方法。 Procedure Click; Override; procedure TLeiLabel.Click;
var s: string;
Begin
Inherited Click;
if FURL = '' then exit;
if LowerCase(Copy(FURL,1,7)) = 'http://' then
s := FURL
else if Pos('@',FURL) <> 0 Then
s := 'mailto:' + FURL
else
s := 'http://' + FURL;
ShellExecute(Application.Handle, 'open', PChar(s), NIL, NIL, SW_SHOWNORMAL);
end;
一般的超链接当鼠标移上时文字的颜色发生变化。为此加上属性HoverColor,表示鼠标移入时文字的颜色。 Property HoverColor : TColor Read FHoverColor Write SetHoverColor default clRed; 还要定义两个接收Windows消息CM_MOUSEENTER和CM_MOUSELEAVE(鼠标移进和移出)的过程: Procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER; Procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; Procedure TLeiLabel.CMMouseEnter (Var Message: TMessage);
begin
If Enabled and Visible and getParentForm(Self).Active then
begin
FFontColor := Font.Color; //将文字的颜色保存起来
Font.Color := FHoverColor; //改变文字的颜色
End;
end;
Procedure TLeiLabel.CMMouseLeave (Var Message: TMessage);
begin
Font.Color:=FFontColor; //恢复文字原来颜色
end;
为设置属性的默认值,还要重载构造Create()(经常还要重载析构Destroy())。 constructor Create(AOwner: TComponent); override; // destructor Destroy; override; 重载构造时必须首先调用祖先类的构造,若要重载析构,则必须要在最后调用祖先类的析构。 constructor TLeiLabel.Create(AOwner: TComponent);
begin
inherited Create(AOwner); //必须首先调用祖先类的构造
FStyle3D := Normal;
FXOffSet := 5;
FYOffSet := -5;
FHoverColor := clRed;
end;
最后,还必须为这个控件加上图标。可以用Delphi中的Image Editor来创建一个24×24的Bitmap位图,保存为一个DCR文件,文件名必须与控件的Pas文件名相同(此处为LeiLabel.pas和LeiLabel.dcr),位图名必须与控件名相同且全部大写(此处为TLEILABEL)。这样,一个完整的实用的控件就做好了。
Windows, Messages, SysUtils, Classes, Controls, ExtCtrls, DesignEditors, DesignIntf;type TMyPanel = class(TPanel)
private
{ Private declarations }
FOnMouseWheel: TMouseWheelEvent;
protected
{ Protected declarations } procedure CMParentColorChanged(var Msg: TMessage); message CM_PARENTCOLORCHANGED;
public
{ Public declarations }
procedure MouseWheelHandler(var Message: TMessage); override;
published
{ Published declarations }
property OnMouseWheel:TMouseWheelEvent read FOnMouseWheel write FOnMouseWheel;
end; TMyPanelEditor = class(TComponentEditor)
procedure ExecuteVerb(Index: Integer); override;
function GetVerb(Index: Integer): string; override;
function GetVerbCount: Integer; override;
end;procedure Register;implementationprocedure Register;
begin
RegisterComponents('GybCtrl', [TMyPanel]);
RegisterComponentEditor(TMyPanel, TMyPanelEditor);
end;{ TMyPanel }procedure TMyPanel.CMParentColorChanged(var Msg: TMessage);
begin
Caption := TMyPanel(Parent).Caption + '的颜色值: $' +
IntToHex(TMyPanel(Parent).Color, 8);
end;procedure TMyPanel.MouseWheelHandler(var Message: TMessage);
var
Handled: Boolean;
begin
Handled := False;
if Assigned(FOnMouseWheel) then
with Message do
FonMouseWheel(Self, TShiftState(Byte(WParam)), WParamHi,
Point(LParamLo, LParamHi), Handled);
if not Handled then
inherited;
end;{ TMyPanelEditor }procedure TMyPanelEditor.ExecuteVerb(Index: Integer);
begin
with TMyPanel.Create(Component.Owner) do
begin
SetBounds(TMyPanel(Component).Left + 20, TMyPanel(Component).Top + 20,
TMyPanel(Component).Width, TMyPanel(Component).Height);
Parent := TMyPanel(Component).Parent;
end;
end;function TMyPanelEditor.GetVerb(Index: Integer): string;
begin
Result := '新建一Panel';
end;function TMyPanelEditor.GetVerbCount: Integer;
begin
Result := 1;
end;end.
如何在父控件属性改变时做出相应的改变:
可以响应的有
CM_PARENTFONTCHANGED = CM_BASE + 8;
CM_PARENTCOLORCHANGED = CM_BASE + 9;
CM_PARENTCTL3DCHANGED = CM_BASE + 17;
CM_PARENTSHOWHINTCHANGED = CM_BASE + 35;
CM_PARENTBIDIMODECHANGED = CM_BASE + 61;但没有一个类似于CM_PARENTTEXTCHANGED的内部消息
2001-03-09· 宋梦?·yesky 可视化控件(Visual Component)实际上就是一个类(class),要编写一个类,可以直接在*.pas文件中编写。但是要编写控件,则必须使用包(package)。从File菜单中选择New,新建一个Package,这就是存放和安装控件用的包。然后单击Package窗口中的Add按钮,添加一个元件(Unit)。 在弹出的对话框最上方选择New Component。因为一个控件的所有属性、方法、事件不可能都由自己编,所以就需要选择祖先类(或者叫做"父类"或"基类"),然后再在其上面添加自己的属性、方法、事件。在Ancestor type后的下拉框中选择所需的祖先类。由于编写可视化控件必须要画图,所以选择TGraphicControl作为祖先类。再在Class Name框中输入新控件(类)的名称,一般以"T"开头。Palette Page是用来选择新控件在Delphi的窗口中的控件页面名称,例如"Standard",这个可以自己取。在Unit File Name中添好新控件文件的路径及文件名,单击OK按钮。新的控件便加入了。现在可以为该控件编写代码了。 下面以编写一个可以自定义图片的滚动条为例,说明编写可视化控件的方法。 按照上面的方法,选择TGraphicControl为祖先类,新控件的名称是TPigHorizontalScroller(小猪水平滚动条)。选择好文件路径和文件名后,单击OK按钮,开始编写代码。 每一个控件,都会被创建(Create)和删除(Destroy),所以必须首先编写这两个过程。对于控件中的每一个过程,都必须在前面先定义,然后再在后面编写。定义的过程或属性有三种:一、在private后定义的是属于控件内部使用的,使用该控件的人无法看到;二、在protected后定义的一般是看不到的,只在别人使用该控件作为祖先类编写其它控件时才可见;三、在public后定义的只允许别人在程序中调用;四、在published后定义的可以在属性窗口(Object Inspector)中看到。由于创建和删除过程除了在编程过程中建立控件时自动执行外,还可能在程序运行过程中动态创建控件时被调用,所以把它定义在public后⑴。(该序号表示次步骤在所附源程序中的代码的位置,下同)现在也许还不知到应该在这两个过程中编写什么,如何去编。我们在下面将会讲到。 我们首先为这个控件添加一些属性。我们定义一个Max属性用于设置或读取滚动条的最大值。因为在程序中一般不直接使用属性,所以要定义一个变量,和该属性对应起来,一边修改或读取其值。因为它只在控件内部使用,所以我们把它定义在private后⑵。(一般与属性相关联的变量都以"F"开头,例如FMax)定义好变量后,再定义属性。这个属性需要再Object Inspector窗口中可见,所以把它定义再published后⑶。定义的语法是: property <属性名>:<类型> read <读取该属性时对应的变量> write <写入该属性时对应的变量或过程>
其它的变量和属性也类似的定义(例如Min最小值、Value当前值等)。下面我们定义几个属性和变量,用于设置滚动条的图片(因为图片变量比较特殊,所以单独讲一下)。我们把LeftButtonUpPicture(向左按钮图片)、LeftButtonDownPicture(向左按钮按下图片)等定义为TBitmap类型(一定要定义相对应的变量)。 大家一定注意到了,在所附的源程序中,定义这几个属性时,read后所指定的读取属性时对应的变量是F…,而write后指定的写入该属性时对应的不是一个变量,而是一个Set…之类的东西,这是一个自定义的过程。作为该功能的过程的定义为:
procedure <过程名>(Value: <被设置的属性的值的类型>) 因为执行写入该类属性的时候需要做其它的事情,所以不能光用一个变量来处理,应该用一个过程来处理。这中过程一般定义在protected后。在该类过程中,使用一个在⑷处这样一个语句来给TBitmap类型的变量来赋值,这是由于该类型的变量不能直接赋值而采用的。 定义完这些TBitmap类型的变量的属性后,上面讲的create过程和destroy过程中就需要编写代码了。因为TBitmap也是一个类,所以在create过程中必须创建⑸,在destroy过程中必须释放掉(free)⑹。这里⑺所指的inherited语句是用于指明该过程是从祖先类类中继承来的。(这个一定不能掉)。 因为我们编写的是可视化控件,所以必须在控件上画图。我们这个控件的祖先类TGraphicControl中封装有一个Canvas(画布)对象,我们可以直接使用它来画图。如果你对画布的使用还不太熟悉,最好去找一本书来看一看。 下面要做的工作就是画图了。如何在控件上画图呢?祖先类TGraphicControl中有一个Paint事件,当控件需要重画时便会自动触发。我们现在要做的就是要为这个事件编写一段程序。首先在protected后定义一个Canvas对象。由于它是祖先类中已有的,所以不需要加任何说明⑻。我们将使用这个对象来画图。接着,就要定义一个Paint过程,编写绘制控件的代码。先在public后定义Paint过程。由于它是由祖先类触发的,而不是由用户调用的,所以后面必须加上override,否则,该控件将会由于Paint过程永远不会被调用而不成为可视化控件⑼。下面我们就来编写Paint过程的代码⑽。 该文章所附的源程序的Paint过程中的T_Height等变量是用来保存滚动条中按钮、滑块等的大小的,这部分程序和普通的Application中的程序差别不大,大部分都是对画布进行操作,相信大家一看就明白。值得注意的是下面对FAutoSize变量的判断⑾,FAutoSize是和该控件的属性AutoSize相关联的布尔型变量,是用来设置这个控件的大小是否随图片的大小而变化的。注意,在控件的代码中,一般都不直接调用属性,而是使用与其相对应的的变量。 程序编到这里,就算是终于给自己的新控件做了一个外型了,不过它还不能滚动。现在我们来编写鼠标事件,让我们能够操纵它。鼠标事件的过程的定义和Paint过程很相似,只是后面要加上参数说明⑿,鼠标事件分为MouseDown、MouseMove和MouseUp三个,在定义后面都要加上override。接下来在后面编写它的代码。注意:这里的鼠标事件是Mouse…,而不是通常的OnMouse…。可是在⒀处的定义是干什么用的呢?这里的事件定义,都是给用户使用的,也就是说,当使用该控件时,会在Object Inspector中的Event页面中显示出来。 这些鼠标事件的代码也非常简单,判断鼠标的坐标,在画布上画出相应的图片等,并同时触发相应的事件。值得注意的是,在调用自定义事件时,都要先用⒁处的这样一个语句来判断用户是否已经为该事件编写代码。这一点非常重要,否则会调用出错。 大家注意到了,刚才所调用的事件都是自定义的,定义的方法也很简单,和定义属性差不多,只是类型时TNotifyEvent罢了。 TNotifyEvent是默认事件,其定义为: TNotifyEvent = procedure(Sender: TObject) 如果你要定义另外形式的事件,就必须这样:先在type后编写 <事件类型名称> = procedure(<参数>:<类型>) 例如: TCustomEvent = procedure(a: Integer; b:String); 然后在public后定义: <事件名称>:<事件类型名称> 例如: AnEvent: TCustomEvent; 看完这些,这整个程序你应该理解了吧。如果编译或运行出错,注意检查以下几点: 1、create和destroy过程中是否有inherited语句; 2、TBitmap类型的变量create和free了没有; 3、过程前有没有控件名,例如:TPigHorizontalScroller.MoseMove 判断鼠标是否进入或离开控件的方法: 定义如下的过程: procedure MouseEnter(var Msg: TMessage); message CM_MOUSEENTER; procedure MouseLeave(var Msg: TMessage); message CM_MOUSELEAVE; 再在下面编写代码就行了。这个方法用于编写三态按钮很有用,有兴趣的话,(将来)可以到我的主页下载Pig VCL Package(小猪可视化控件包),参考其中的TPigButton控件。(现在还没有,但是将来有了,欢迎大家下载)下载源码
一般来说控件内容纳其它控件时,通常将子控件的Owner设置为父控件(构造是指定),Parent当然也是父控件(容器),IDE是根据Owner判断拦截其消息的,只要将子控件的Owner设置为空,就可以在设计时进行操作,如同容器外的其它独立控件一样,前面我已经说过了:“对新生成的控件不要指定Owner(为空),只需要指定parent,但是必须在析构函数中显式的销毁该对象”
你可以看看CM_TEXTCHANGED,CM_FONTCHANGED等类似的消息并相应它们。设计时菜单:
你可以看看TComponentEditor的帮助文件。主要是定义一系列的动词,然后在回调函数终处理用户的点击。
问题1的解决:重载Loaded过程,在其中输入
procedure TMacXForm.Loaded;
var TheWnd: HWND;
begin
inherited Loaded;
if not (csDesigning in ComponentState) and (FForm <> nil) then
begin
if FForm <> nil then
begin
TheWnd := FForm.Handle;
FWndProcInstance := MakeObjectInstance(FormWndProc);
FDefProc := SetWindowLong(TheWnd,GWL_WNDPROC,LongInt(FWndProcInstance));
end;
end;
end;
其中的定义:
FForm: TCustomForm; //就是TCustomForm(Parent)
FWndProcInstance: Pointer;
FDefProc: LongInt;
然后在procedure TMacXForm.FormWndProc(var Message: TMessage);中就会接收到Parent的消息
创建控件时并非是像 Lewolf(李狼) 说的那样将Owner设为nil,而是设为IDesignerHook对象的GetRoot,即
ClientPanel := TMacXFormBody.Create(FForm.Designer.GetRoot);