在单独的设计时包中覆盖组件创建构造函数
Posted
技术标签:
【中文标题】在单独的设计时包中覆盖组件创建构造函数【英文标题】:Overriding Component Create Constructor in Separate Design-Time package 【发布时间】:2012-03-02 23:23:04 【问题描述】:编程环境:Delphi 6 及以上
我知道自 Delphi 6 以来,自定义组件必须具有单独的设计和运行时包。因此,组件的所有运行时功能都必须在一个单独的单元中,并单独打包到组件的设计时包中。
我的问题如下:我的组件的代码需要在运行时在窗体上创建时运行,另外还有一些代码需要在设计时运行,当组件放置在形式。我已经设法将运行时代码放入单独的运行时单元中,打包并成功部署。
但是,在单独的设计时模块单元中,当组件被放置到表单上时,如何在设计时引用和添加需要包含在组件的创建构造函数中的设计时代码?
【问题讨论】:
它们不必在单独的单元或包装中,您可以随时查看if csDesigning in ComponentState then YouAreAtDesignTime
。
TLama 是正确的。设计时代码的问题在于,某些 ToolsAPI 单元无法在不违反许可协议的情况下链接到运行时包(因此您显然不应该这样做)。只要您不使用这些单元,完全有可能在您自己的单元中包含您自己的设计时间代码。
您能否更具体地说明您想在构造函数中做什么?为什么它需要在构造函数中?
如果整个 VCL 可以在不错误地使用其组件中的设计时功能的情况下构建,那么您的也可以。绝对没有理由将 DesignIDE/DesignIntf 功能混合到您的运行时代码中;应该始终可以正确地将它们分开。
谢谢 NGLN。在设计时将组件拖放到表单上时,我将有关父表单的详细信息写入数据库。要访问此信息,需要不能包含在运行时接口中的 DesignIDE 例程,因为在编译包时,会出现 DesignIDE.dcu' not found。因此,需要一个单独的设计时构造函数。谢谢
【参考方案1】:
您可以将设计时行为与运行时行为分开
if [not] (csDesigning in ComponentState) then
但是如果您的构造函数代码需要 DesignIDE
设计时包,例如来自DesignEditors
、DesignIntf
等单位,那么我认为你被卡住了。也许一些 IOTA 的参与会有所帮助。但由于似乎不存在用于创建组件的通知程序接口,因此需要自定义 IOTAFormEditor
。没那么容易,如果不是不可能的话。
【讨论】:
对不起,我应该提到的是,设计时构造函数中所需的代码确实使用了 DesignIDE 功能,因此需要分开。您所说的“也许 IOTA 的参与会有所帮助”是什么意思? 简短回答:不要。通过在组件构造函数中使用 DesignIDE 功能,总有办法解决您尝试解决的任何问题。 @user1202134,为什么您的构造函数需要 DesignIDE 功能?绝对没有必要这样做,如果你这样做了,那你就做错了。 谢谢肯。当我在设计时将组件放到表单上时,我需要存储还需要将项目名称存储到数据库中。我看到可以在设计时获取项目名称的唯一方法是使用在 ToolsAPI 中找到的 GetActiveProject.FileName。此外,我正在将一个组件套件(DesignerForms-Guild)从 Delphi5 迁移到 Delphi2005。该组件使用 DesignIDE 中的许多调用,但当前不在单独的运行时和设计时包中。因此,当这些组件被放置在应用程序中时,在编译时,就会出现 DesignIDE.dcu not found 错误。 您可能应该专门针对 DesignerForms-Guild 提出一个新问题。如果这是一个第 3 方组件套件,它以一种不符合 Borland/CodeGear/Embarcadero 的 Delphi 团队的意图的方式使用设计时功能,则存在无法轻松移植到更高版本的 Delphi 版本的风险,但必须从头开始重新设计和重新实现。【参考方案2】:为什么不使用回调?
从您的设计时包初始化代码中执行以下操作:
unit MyDsgnUnit;
interface
//TMyHook defined in AnImplUnit
TMyDesignHandlerObject = class
procedure MyMethod(Sender:TObject;ParentForm:TObject); must match TMyHook
end;
implementation
uses AnImplUnit, DesignUnitNamesHere;
procedure TMyDesignHandlerObject.MyMethod(Sender:TObject);
var
newObject:TMyComponent;
begin
newObject := TMyComponent(Sender);
DoSomethingThatneedsDesigntimeStuff(newObject);
end;
finalization
ADesignHandlerObject.Free;
initialization
ADesignHandlerObject := TMyDesignHandlerObject.Create;
AnImplUnit.AfterConstructionHook := TDesignHandlerObject.MyMethod;
并在您的组件中执行以下操作:
unit AnImplUnit;
interface
type
TMyHook = procedure(Sender:TObject;ParentForm:TObject) of object;
var
AfterConstructionHook:TMyHook;
implementation
...
procedure TMyComponent.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
DOMyStuff;
if Assigned(AfterConstructionHook)
AfterConstructionHook(Sender,Parent);
end;
更新 更多示例。没有理由不能向 AfterConstructionHook 添加更多参数,但是由于您对 Sender 的引用已经是 TMyComponent 类型,所以当您可以从内部访问 TMyComponent(Sender)
中的所有公共或受保护的内容时,我看不出重点你的钩子函数,如果你在本地继承(称为受保护的访问类),你也可以访问受保护的东西。
【讨论】:
您好 Warren P,感谢您的热心投入。这似乎是一个可行的解决方案。由于我对这个概念不太熟悉,您能否再详细说明一下,也许给我一个简单但完整的例子?谢谢。 上面的例子差不多完成了。我再补充一点。 非常感谢 Warren P。还有一个问题,是否可以在“AfterConstructionHook”中有一个附加参数发送到回调? 当然可以。但是,“为什么?”你想做什么? 感谢您的热心输入 Warren P。我正在慢慢到达那里。当组件被拖放到表单上时(创建构造函数被触发),它会检查它需要的父组件是否存在。如果它不存在,那么它必须在那时创建它。为了创建它,我使用了 IDesigner.CreateComponent 方法。但是,此方法具有参数,即组件类及其父组件。因此,这些需要通过 AfterConstructionHook 传递。因此,如何在回调中包含参数?谢谢【参考方案3】:确保您的DPK
定义了您选择的符号,例如DESIGNTIME
。然后,您将能够使用类似这样的方法仅在需要时包含设计时单元:
uses Windows, Whatever, Something
$IFDEF DESIGNTIME
,DesignIntf
$ENDIF
;
然后在你的构造函数代码中相同:
constructor TMyClass.Create(aOwner:TComponent);override;
begin
inherited;
$IFDEF DESIGNTIME
// I'm at design time.
$ENDIF
end;
使用此技术时,您应该为您的 pacakge 和正常的可执行文件使用单独的 DCU
目录,或者每次从设计时包切换到其他项目时执行 build
。那是因为如果PAS
发生了变化,Delphi 只会重新构建DCU
,而在这种情况下,PAS
并不能说明全部情况,定义的符号也很重要。如果您在构建设计时项目时在磁盘上有一个由 Delphi 编译的 DCU
,则在尝试编译普通项目时可能会看到 ToolsApi.DCU not found
。重建会重新编译 DCU 并使消息消失。同样,如果您在构建普通项目后重新编译(而不是重新构建)设计时项目,您的 DCU 可能会卡在它的非 DESIGNTIME 状态,从而使您没有特殊的设计时行为。
【讨论】:
您好 Cosmin,非常感谢您的意见。我会考虑做你所说的。我只是想澄清一下。我是否仍需要将运行时和设计时划分为单独的单元和包以分别编译?谢谢。 这完全取决于您,并且可以有条件地编译入(或编译出)的代码数量(或种类)没有限制。只是不要过度使用它,因为你最终会得到一些难以阅读和维护的东西。 条件定义将成为一种不好的方法,如果您还想使用适当的运行时包来拥有一个纯设计时包,并且运行时包不仅会由 IDE 加载,还会由由您自己的应用程序,如果您构建任何使用运行时 BPL 包而不是静态链接的应用程序。 如果没有条件定义,您需要设置一个始终存在并被检查的钩子,即使在运行时不再需要它时也是如此。我个人不使用运行时包,所以我更喜欢这种折衷方案。尽管如此,这是需要考虑的事情,因此请为评论 +1。 这就是我的回答提到使用回调(或挂钩)的原因。【参考方案4】:只要你的代码不需要IDE工具,比如设计界面等,那么你只需要检查组件标志,你就可以在组件内的任何地方使用它,如下...
procedure TNewEdit.Loaded;
begin
inherited;
if (csDesigning in ComponentState) then
ShowMessage('Designing')
else
ShowMessage('Running');
end;
但是,在不知道您要做什么的情况下,有几扇门是敞开的……例如,如果您想在设计时更改属性值,而在运行时更改不同的值,则可以使用流式传输你必须处理的问题。
【讨论】:
以上是关于在单独的设计时包中覆盖组件创建构造函数的主要内容,如果未能解决你的问题,请参考以下文章