我有几个自定义VCL组件,它们在重写TComponent Loaded()方法时执行重要任务。这在动态创建实例时会产生麻烦,因为在运行时Delphi全局加载器不会调用Loaded()方法,就像在设计时放置在窗体/框架上的组件一样。我还必须将Loaded覆盖放在类声明的 public 部分中,这样无论创建组件实例的代码都可以调用它。最后,我必须记得为动态创建的实例调用Loaded(),否则细微的错误会蔓延到应用程序中,这个问题已经让我好几次了。
是否有更好的解决方案或方法?
答案 0 :(得分:3)
如果您需要在代码中调用Loaded,那么您做错了。如果你依赖于第三方控制,那么我会修复那个人的控制权。请参阅下文。
让我构成一个假设的例子:假设我有5个已发布的属性,一旦它们全部加载,可以生成复杂的曲线甚至更好,生成分形,这需要很长时间。
在设计时,我想在加载后立即预览此曲线,但我不希望在DFM流式传输过程中重新计算曲线5次,因为每个参数P1到P5(类型为Double) )有一个SetP1方法,它调用一个名为Changed的受保护方法,并重建我的曲线。相反,如果csDesigning或csLoading处于组件状态,我会返回SetP1方法,然后我从Loaded调用一次Changed。显然,在所有情况下,我都不能单独依赖属性设置器方法来调用所有更改。所以我需要Loaded告诉我做第一代一些昂贵的工作,我想要完成一次,而不是N次,其中N是已经加载的具有方法设置过程的DFM属性的数量调用名为Changed的方法或类似的方法。
在您的情况下,在运行时,您根本不应该依赖于Loaded被调用。相反,您应该将属性集方法调用Changed。如果你需要某种方法一次更改多个属性,然后只做一次昂贵的事情,那么实现TMyComponent.BeginUpdate / TMyComponent.EndUpdate类型的方法调用,并避免额外的工作。
我认为没有任何有用的地方,从Loaded做一些事情是有意义的,除了上面的情况,这应该是特定于设计时和基于DFM的类使用。我希望正确设计的TComponent或TControl只需通过在代码中创建并通过设置其属性来正确初始化自己。
因此,对于我假设的TMyFractal组件,我会在代码中创建它而不使用DFM加载或调用Loaded:
cs := TMyFractal.Create(Self);
cs.Parent := Self; {Parent to a form}
cs.Align := alClient;
cs.BeginUpdate;
cs.P1 := 1.03; // does NOT trigger Regenerate
cs.P2 := 2.3;
cs.P3 := 2.4;
cs.P4 := 2.5;
cs.EndUpdate; // triggers expensive Regenerate method .
cs.Show;
// later someone wants to tweak only one parameter and I don't want to make them
// call regenerate:
cs.P5 := 3.0; // Each param change regenerates the whole curve when not loading or in a beginupdate block.
在我的TMyFractal.Change方法中,我会调用昂贵的RegenerateCurve方法一次,每次在运行时修改任何系数P1-P4,在初始设置之后,并且恰好当组件从DFM流入时,其中Loaded是仅用于处理这样一个事实:我几乎不能指望在我的控件中执行beginupdate / endupdate,就像我在上面的代码中所做的那样。