如何动态创建 继承的窗体(100)

D

delhpi

Unregistered / Unconfirmed
GUEST, unregistred user!
unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls;type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end;var Form1: TForm1;implementationuses RegUnit, EdtUnit;{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);begin with TEditEx.Create(self) do//继承的Edit运行正常 begin parent:=self; end;end;procedure TForm1.Button2Click(Sender: TObject);begin with TForm.Create(Application) do//直接用TForm也正常 begin show; end;end;procedure TForm1.Button3Click(Sender: TObject);begin with TFormReg.Create(Application) do//这里出错 begin show; end;end;end.==============unit EdtUnit;interfaceuses StdCtrls;type TEditEx = class(TCustomEdit);implementationend.=====================unit RegUnit;interfaceuses Forms;type TFormReg = class(TCustomForm);implementation{ TFormReg }end.
 
问题在这TFormReg = class(TCustomForm);TForm类不同于其它的控件,它一般是需要引用同名有资源文件的,就是{$R *.dfm}。如果你想见一个继承的窗体类,你可以首先从TFrom上创建一个新的窗体,然后再继承下来。因为这个资源文件IDE已经给你建好。给你个资源文件,看看这把好用不将{ TFormReg }改为{$R *.dfm}。创建一个文件为RegUnit.dfm的文本文件,将下述的内容拷入,放在源文件的目录下object FormReg: TFormReg Left = 192 Top = 114 Width = 192 Height = 192end成功就要给分,别太监[:D]
 
谢谢。但是procedure TForm1.Button2Click(Sender: TObject);begin with TForm.Create(Application) do//直接用TForm也正常,我也没有一个和类同名的资源文件啊 begin show; end;end;帮助里有这样的解释。Use CreateNew instead of Create to create a form without using the associated .DFM file to initialize it. Always use CreateNew if the TCustomForm descendant is not a TForm object or a descendant of TForm.他的意思是,假如TCustomForm的后代不是TForm或者TForm的后代的话,就不能用 Create,而要用CreateNew.也就是只有TForm才可以Form.Create或者从TForm继承下来的类,比如 TForm1 = class(TForm),才可以TForm1.Create不知道我是否理解错了?我现在修改为下面的,还是不能用createunit RegUnit;interfaceuses Forms;type TFormReg = class(TForm);//从TForm继承implementationend.只有这样才行。unit RegUnit;interfaceuses Forms,Unit1;type TFormReg = class(TForm1);implementationend.
 
抱歉,我刚才是根据报错提示给你的答复。刚才的说法并不严谨。看看TForm的创建源码constructor TCustomForm.Create(AOwner: TComponent);begin GlobalNameSpace.BeginWrite; try CreateNew(AOwner); if (ClassType <> TForm) and not (csDesigning in ComponentState) then begin Include(FFormState, fsCreating); try if not InitInheritedComponent(Self, TForm) then raise EResNotFound.CreateFmt(SResNotFound, [ClassName]); finally Exclude(FFormState, fsCreating); end; if OldCreateOrder then DoCreate; end; finally GlobalNameSpace.EndWrite; end;end;就是说每次的创建已经调用了CreateNew的方法,而CreateNew的方法就是取代资源文件的作用,做一些表单初始化的工作。if not InitInheritedComponent(Self, TForm) then这一行就解释了为什么只能从TForm类中派生了。具体的可以看InitInheritedComponent的帮助。function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean; function InitComponent(ClassType: TClass): Boolean; begin Result := False; if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit; Result := InitComponent(ClassType.ClassParent); Result := InternalReadComponentRes(ClassType.ClassName, FindResourceHInstance( FindClassHInstance(ClassType)), Instance) or Result; end;var LocalizeLoading: Boolean;begin GlobalNameSpace.BeginWrite; // hold lock across all ancestor loads (performance) try LocalizeLoading := (Instance.ComponentState * [csInline, csLoading]) = []; if LocalizeLoading then BeginGlobalLoading; // push new loadlist onto stack try Result := InitComponent(Instance.ClassType); if LocalizeLoading then NotifyGlobalLoading; // call Loaded finally if LocalizeLoading then EndGlobalLoading; // pop loadlist off stack end; finally GlobalNameSpace.EndWrite; end;end;//这个sub函数是个关键!!!!!!能解释你全部的疑惑------------------------------------- function InitComponent(ClassType: TClass): Boolean; begin Result := False; if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit; Result := InitComponent(ClassType.ClassParent); Result := InternalReadComponentRes(ClassType.ClassName, FindResourceHInstance( FindClassHInstance(ClassType)), Instance) or Result; end;读懂这个函数就能告诉你为什么从TForm直接继承下来还是不好用。为什么我只要添加相应的dfm文件就不报错了。 Result := InitComponent(ClassType.ClassParent); Result := InternalReadComponentRes(ClassType.ClassName, FindResourceHInstance( FindClassHInstance(ClassType)), Instance) or Result;告诉你,你就是直接从TForm继承也不行,除非你有相应的资源文件支持!
 
从源码看,任何Form的创建都是先调用CreateNew,就是无资源文件的情况下,先创建一个普通的窗口。接下来, 如果if (ClassType <> TForm) and not (csDesigning in ComponentState) then就结束了。也就是说只有TForm才能在无资源文件的情况下,TForm.Create(),创建成功。如果是其他类,比如从TForm继承下来的TForm1,就是我们最常见的,或者其他派生类,比如TFormMy1 = Class(TForm) 或者 TFormMy2 = Class(TCustomForm),等都要 if not InitInheritedComponent(Self, TForm) then raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);这个大致意思是,初始化继承的组件=InitInheritedComponent(Self, TForm),当然如果没有相应的资源文件的话,就是抛出异常。InitInheritedComponent(Instance: TComponent; RootAncestor: TClass)对于TForm1的话,就是InitInheritedComponent(TForm1, TForm)接下来 Result := InitComponent(Instance.ClassType);对于TForm1的话就是 Result := InitComponent(TForm1);这个过程好像是个递归的调用。function InitComponent(ClassType: TClass): Boolean; begin Result := False; if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;如果追溯到TComponent类了,或者当前类是RootAncestor,即TForm了,就退出。对于TForm1来说,第一次进入时,不会满足上面那行代码,(TForm1 <> TComponent) or (TForm1 <> TForm)所以会继续向下运行。 Result := InitComponent(ClassType.ClassParent);//这里递归对于TForm1来说,这时ClassType.ClassParent就是TForm。第二次进入function InitComponent(ClassType: TClass): Boolean;这时满足条件了,退出递归,执行点返回到递归语句下一行,递归的结果Result是False。 Result := InternalReadComponentRes(ClassType.ClassName, FindResourceHInstance( FindClassHInstance(ClassType)), Instance) or Result;这里函数按照字面意思是,内部的读取组件资源。对于TForm1来说,InternalReadComponentRes(TForm1, FindResourceHInstance( FindClassHInstance(TForm1)), Instance) 就是从dfm里读取TForm1类的资源,读取成功就ok,否则就抛出异常=就是前面提到的。object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 270 ClientWidth = 546 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13具体这个函数我也看不懂,不知道Form上的其他控件是在什么地方读取的。不过Form的窗口过程大致了解了一点了。
 
不都写的很明白了么object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 270 ClientWidth = 546 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13如果包含其它控件,就在这个资源文件里了。
 
我知道是在这个文件里,不过我看代码,还没有明白其他控件是在什么地方读取的。或者说对读取窗体以及里面的控件资源这部分代码好没有大致的领悟。
 
窗体里面的组件资源也在这个 .dfm 文件里了. 从代码去看的话, 那你要从 InternalReadComponentRes() -> ReadComponent() -> Reader.ReadRootComponent() 在这里面你就可以看到它循环加载里面的组件了
 
Reader.ReadRootComponent() 暂时还没有看出名堂。
 
接受答案了.
 
顶部