在组件里替换FORM的onCreate事件函数时遇到的问题(100)

  • 主题发起人 主题发起人 HsyPrg
  • 开始时间 开始时间
H

HsyPrg

Unregistered / Unconfirmed
GUEST, unregistred user!
自定了一加在FORM上的管理组件: 重载了组件的Notification过程: procedure TFormLed.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if fAutoAuthorized then case Operation of opInsert: if (AComponent is TCustomForm) and (AComponent <> owner) then begin fOldOnFormCreate := TForm(AComponent).OnCreate; TForm(AComponent).OnCreate := OnFormCreate; end; end; end; 现将组件放在FORM1中,在一个button的onclick事件中 oForm2 := Form2.Create(self); 如此,Form2.OnCreate事件将被 FomrLed组件的OnFormCreate 替换,成功达到效果。 现在问题来了,当我在FORM2的OnCreate事件响应过程Form2OnCreate中写入代码时,Form2OnCreate最终将 FormLed.OnFormCreate覆盖,FormLed.OnFormCreate将不再能够执行得到。 请问各位高手,有什么办法可以解决;
 
对VCL源码研究不够,不知道FORM在哪个步骤将用户写的FormOnCreate事件过程提交到FOnCreate;
 
刚才写错了,更改一下: inherited Notification(AComponent, Operation); case Operation of opInsert: if (AComponent is TCustomForm) and (AComponent.Owner=SELF) AND (AComponent<>SELF) AND (AComponent <> owner) then begin// fOldOnFormCreate := TForm(AComponent).OnCreate;// TForm(AComponent).OnCreate := FormCreate; FormCreate(AComponent); //此处直接调用你的函数。 end; end;跟踪发现,Form2创建的时候,先触发Owner.Notification,然后才从dfm中装载属性(含Name、OnCreate、OnClose),所以此处你可以发现TForm(AComponent).Name是空,还没装载。OnCreate当然也是nil,你可以这样判断 if Not Assigned(TForm(AComponent).OnCreate) then showmessage('no oncreate');,可以发现无论form2是否有OnCreate,本处都提示no oncreate.
 
TO znxia,  谢谢你的回答,我之前也是这么做的,但这样做的话不能达到我的要求。为什么呢,因为那时只有FORM2能访问,FORM2里的组件都没创建,而我要操作的正是FORM2里的组件。  后面我用的方法,效果是可以达到的,问题是FORM2不能再写OnCreate事件了,所以还是不完美。  而你文中提到的总是提示nocreate中是对的,FORM2的oncreate事件将在Notification后面的某个地方才会被装载。(具体是在哪个地方装载应该就是解决此问题的关键了)
 
TForm的Create是这样写的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;//调用OnCreate end; finally GlobalNameSpace.EndWrite; end;end;由此可以看出,在其装载完属性后、调用OnCreate前,系统并没有开放任何函数或者消息可供我们来处理自己的代码,我现在只能做的Form2窗体调用完Form2.OnCreate之后,再调用你的OnCreate,如下:const wm_newform = wm_user + 1;-- procedure WMNewForm(var Message: TMessage); message wm_newform;procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);begin inherited Notification(AComponent, Operation); case Operation of opInsert: if (AComponent is TCustomForm) and (AComponent.Owner = SELF) and (AComponent <> SELF) and (AComponent <> owner) then begin Postmessage(Self.Handle, WM_NewForm, Integer(AComponent), 0); end; end;end;procedure TForm1.WMNewForm(var Message: TMessage);var Fm: TForm;begin FM := TForm(Message.WParam); FormCreate(FM); //注意此时已经完成了Form2.OnCreate的调用,因此我就直接调用该函数end;
 
TO znxia, 谢谢你的答案,但由于我写是是一个放在FORM上组件,所以没办法通过MESSAGE截获到Windows消息; 受你的启发,我尝试在Notification中用DELPHI的持续性机制对FORM2进行读取:function SimpleReadComponentRes(const ResName: string; HInst: THandle): Boolean;var HRsrc: THandle; szvalue: string; oRs: TResourceStream; oS: TStringStream; oList: TStringList;begin if HInst = 0 then HInst := HInstance; HRsrc := FindResource(HInst, PChar(ResName), RT_RCDATA); Result := HRsrc <> 0; if not Result then Exit; with TResourceStream.Create(HInst, ResName, RT_RCDATA) do try //这里可以读出form2的OnCreae 为 FormCreate过程; //也能用ClassType.MethodAddress('FormCreate') 获是过程地址; finally Free; end; Result := True;end;procedure TSFEFormLed.Notification(AComponent: TComponent; Operation: TOperation);begin inherited Notification(AComponent, Operation); if Assigned(owner) and fAutoAuthorized then case Operation of opInsert: if (AComponent is TCustomForm) and (AComponent <> owner) then with AComponent do try SimpleReadComponentRes(ClassType.ClassName, FindResourceHInstance( FindClassHInstance(ClassType))); except end; end;end;如SimpleReadComponentRes说明,工作只进行到这一步就卡壳了,因为即使读出了Form2.FormCreate地址,我也没有办法替换它或者阻止delphi后面对OnCreate事件字段时行覆盖;或许是我太追求完美了,今天我将再尝试一天,如不能解决就放弃这一功能,上来结帖,再次感谢!
 
type TFormLed = class(TComponent) …… protected procedure Loaded;override; …… end;procedure TFormLed.Loaded; begin inherited; //在这里进行OnCreate的替换end;procedure TFormLed.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if fAutoAuthorized then case Operation of opInsert: if (AComponent is TCustomForm) and (AComponent <> owner) then begin fOldOnFormCreate := TForm(AComponent).OnCreate; TForm(AComponent).OnCreate := OnFormCreate; end; end; end; 提醒你一下,Notification会执行很多次(关于Notification是怎么执行的,自己看VCL代码去),你用fOldOnFormCreate保存的是最后一次,有什么用?你可能把很多Form的OnCreate替换了,但是只保存了其中一个的原有OnCreate方法指针。
 
呵呵,是我钻牛角尖了。问题最终通过对替换Screen对象的OnActiveFormChange事件过程来完成作业。有时候把问题想得太复杂了。谢谢上面两位,结帖;
 
后退
顶部