自定义组件:将事件推迟到 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 之后的主要内容,如果未能解决你的问题,请参考以下文章

Vue组件之实现自定义事件

#夏日挑战赛# HarmonyOS - 自定义组件之slider滑块

vue自定义事件将组件内的数据传输到实例中去使用

vue之props和自定义事件的驼峰命名

无法在 Vue.JS 上捕获自定义事件

。。。事件注册方法之五。。。