【发布时间】:2010-10-09 15:13:33
【问题描述】:
Delphi 组件有CreateWnd 和CreateWindowHandle(以及DestroyWnd 和DestroyWindowHandle)。他们都打算被后代覆盖,对吧?并且不打算被底层 VCL 实现调用?
它们之间有什么区别;什么时候应该覆盖它们中的任何一个?
【问题讨论】:
标签: delphi components
Delphi 组件有CreateWnd 和CreateWindowHandle(以及DestroyWnd 和DestroyWindowHandle)。他们都打算被后代覆盖,对吧?并且不打算被底层 VCL 实现调用?
它们之间有什么区别;什么时候应该覆盖它们中的任何一个?
【问题讨论】:
标签: delphi components
到目前为止,这里的大多数答案都差不多,您最好听从他们的建议。但是,这个故事还有更多内容。对于您何时会覆盖其中一个或另一个的具体问题,我将尝试简要介绍一下。
CreateParams();
一般来说,大多数时候您真正需要做的就是重写 CreateParams()。如果您只想对现有控件类进行子类化(还记得 Windows 风格的“子类化吗?”请参阅 Petzold 关于 Windows 编程的开创性工作)并将其包装在 VCL 控件中,您可以从 CreateParams 执行此操作。您还可以控制设置的样式位和其他各种参数。我们使创建“子类”的过程变得非常简单。只需从您的 CreateParams() 方法调用 CreateSubClass()。有关 TCheckBox 或 TButton 等示例,请参阅核心 VCL 控件。
CreateWnd();
如果您需要在创建窗口句柄后对其进行更多操作,您可以覆盖它。例如,如果您的控件是某种列表、树或其他需要创建后配置的控件,您可以在此处执行此操作。调用继承的 CreateWnd,当它返回时(如果你从 CreateWnd 返回,你知道你有一个有效的句柄,因为如果出现问题它会引发异常),只需应用你的额外魔法。一个常见的场景是获取缓存在实例 TStrings 列表中的数据,并将其实际移动到底层窗口控件中。 TListBox 就是一个典型的例子。
CreateWindowHandle();
我不得不去刷新我对这个的记忆,但似乎这是一个很少,如果有的话,被覆盖。在 VCL 本身内部的少数情况下,它似乎用于通过一些控件(例如 TEdit 和 TMemo)来解决特定的 Windows 版本和区域设置异常。另一个更明确的案例是 TCustomForm 本身。在这种情况下,它支持旧的 MDI(多文档接口)模型。在这种情况下,无法使用普通的 CreateWindowEx() API 创建 MDI 子项,您必须向 MDI 父框架发送消息才能实际创建句柄。因此,重写此方法的唯一原因是,如果创建句柄的实际过程是通过与旧的久经考验的 CreateWindowEx() 完全不同的方式完成的。
我确实注意到您的问题只是询问创建过程,但在某些情况下,对于句柄破坏和有时围绕句柄重新创建的“巫毒”,有相应的方法被覆盖。但这些是应该单独讨论的其他主题:-)。
【讨论】:
我确信最终的答案只能来自参与创建 VCL 的人员(Allen?),但恕我直言,应该覆盖调用链中责任最小/最低的虚拟方法.这就是为什么我总是重写 CreateParams() 和 CreateWindowHandle()。这看起来很合适,因为它们都被 CreateWnd() 调用,并且都只做一件特殊的事情。
最终这可能是一个偏好问题。
【讨论】:
谁做什么:
CreateWnd 是为 WinControl 创建完整窗口的总承包商。
首先,它必须通过调用 CreateParams 为 WindowClass 设置所需的属性,并确保它已正确注册。
然后它通过调用 CreateWindowHandle 来获取实际创建的窗口,该方法从操作系统返回生成的句柄。
之后,我们有一个能够处理消息的有效窗口,CreateWnd 进行最后的修饰,调整不同的视觉方面,如大小、字体等。
在 CreateWnd 完成后,CreateHandle 还会完成后续步骤,以帮助 VCL 管理其窗口(标识、亲子关系...)。
【讨论】:
CreateWnd 首先调用 CreateParams,然后使用创建的 Params 调用 CreateWindowHandle。通常,您将覆盖 CreateWnd 和 CreateParams 而不是 CreateWindowHandle。
我希望这会有所帮助!
【讨论】: