自定义组件:将事件推迟到 FormCreate 之后
Posted
技术标签:
【中文标题】自定义组件:将事件推迟到 FormCreate 之后【英文标题】:Custom Component: Deferring events till after FormCreate 【发布时间】:2019-08-20 21:09:46 【问题描述】:我创建了一个从 TTreeView 派生并自动填充专门内容的组件。我添加了自己的 OnSelectionChange 事件。当句柄已分配且 ComponentState csReading 或 csLoading 时,从 Change 方法(覆盖 TTreeView.Change 方法)中调用 OnSelectionChange。
问题是当组件添加到表单时,OnChange 事件会在 FormCreate 之前发生。我怎样才能将我的事件延迟到所有创建完成之后?
我想我可以向组件发布一条消息并对此做出反应,但是我只想在处于创建状态时发布消息。有没有更好的办法?
procedure TMyDescendentTreeView.Change(Node: TTreeNode);
begin
inherited;
if HandleAllocated and assigned( fOnSelChange) and (not ( csReading in ComponentState ))
and (not ( csLoading in ComponentState )) then
fOnSelChange( Self, TXYZ(Node).Data, TXYZ(Node) );
end;
【问题讨论】:
您的组件不应使用 OnChange 事件。该事件适用于您的组件的用户。您应该改用Change
,在那里您可以对csReading/csLoading 进行测试,然后您的处理程序可以调用用户的OnChange(如果已分配)。请参阅任何 Delphi VCL 组件作为示例,使用任何 TNotifyEvents(例如 TButton.Click)。
发生选择更改时已调用 OnChange。您引入新事件处理程序的原因是什么?
我无法重现这个。库存树视图的 OnChange 在其所在表单的 OnCreate 之后触发。
@Zax 请注意,Loaded()
仅对从 DFM 流式传输的组件调用。如果用户在代码中动态创建你的组件,Loaded()
将永远不会被调用,你的fInitialized
也不会被设置为 true。
您现在已多次被要求提供minimal reproducible example 来证明该问题,但您仍然没有这样做。在您获得帮助之前,您很可能不会获得帮助,help center 指南要求在请求调试帮助的问题中使用该代码。
【参考方案1】:
不要在设计时将OnChange
处理程序分配给您的组件。当表单准备好时,让表单在其OnCreate
事件中在代码中动态分配处理程序:
procedure TMyForm.FormCreate(Sender: TObject);
begin
//...
MyTreeView1.OnChange := MyTreeView1Change;
// optional, call the event now...
MyTreeView1Change(MyTreeView1, MyTreeView1.Selected);
//...
end;
procedure TMyForm.MyTreeView1Change(Sender: TObject; Node: TTreeNode);
begin
//...
end;
【讨论】:
谢谢 Remy,但这需要用户知道我的组件以一种意想不到的方式运行,需要调整他们的代码来解决它 那么不要把你的组件写成“以意想不到的方式运行”。你真正想解决什么?这听起来像是XY problem。 自定义树视图包含专门的内容,并在创建过程中填充。组件的用户将需要使用 OnSelectionChange 对所选节点的更改做出反应。如果 OnSelectionChange 发生在表单控制和变量初始化都没有发生的表单上,那将是糟糕的设计。 @Zax "在创建过程中被填充" - 为什么?你到底在填什么,什么时候填? “如果 OnSelectionChange 发生在表单控制和变量初始化都没有发生的表单上,那将是糟糕的设计”——然而,Delphi 的对象设计允许这样做。更糟糕的情况是,用户可以在调用inherited
构造函数调用 DFM 流之前覆盖 Form 的构造函数来初始化变量。
@Zax 你不应该试图让你的组件比它应该的更智能。如果您的组件发生了变化,并且它已经分配了一个希望收到更改通知的处理程序,只需调用它即可。如果需要延迟初始化,让处理程序处理问题。【参考方案2】:
好的,感谢 Remy Lebeau,他帮助我更好地理解了我的问题。因为我的组件默认填充了内容,所以我丢弃了初始填充期间发生的更改通知(一旦句柄可用就会发生)。
所以现在选择更改事件不再发生在组件创建期间(因此在 FormCreate 之前)。
【讨论】:
以上是关于自定义组件:将事件推迟到 FormCreate 之后的主要内容,如果未能解决你的问题,请参考以下文章