嵌入TFDQuery的自定义控件无法将持久化字段保存至DFM
问题分析与解决方案
你遇到的核心问题是:自定义组件内部的TFDQuery子组件,其持久化字段无法保存到DFM文件,仅在表单类声明中生成字段代码,但无对应DFM对象。以下是具体原因和修复步骤:
核心原因
虽然你给FEntityData调用了SetSubcomponent(True),但设计器并未将它的持久化字段视为父组件的一部分进行持久化,主要遗漏了以下关键环节:
- 父组件未重写
GetChildren和SetChildOrder方法,设计器无法枚举到内部子组件; EntityData属性的Assign方法默认不会复制持久化字段,且设计时子组件需要被设计器直接管理,而非通过赋值覆盖;- 未标记子组件为持久化状态,设计器不会将其属性写入DFM。
具体修复步骤
1. 重写父组件的子组件枚举方法
设计器通过GetChildren和SetChildOrder识别组件的子组件,需要将内部的FEntityData和FEntityDatasource暴露给设计器:
type TKpDBEntityEdit = class(TdxPanel) private ... protected // 添加以下两个方法 procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; procedure SetChildOrder(Child: TComponent; Order: Integer); override; ... public ... published ... end; implementation procedure TKpDBEntityEdit.GetChildren(Proc: TGetChildProc; Root: TComponent); begin inherited; if FEntityData <> nil then Proc(FEntityData); if FEntityDatasource <> nil then Proc(FEntityDatasource); end; procedure TKpDBEntityEdit.SetChildOrder(Child: TComponent; Order: Integer); begin if Child = FEntityData then FEntityData.Tag := Order else if Child = FEntityDatasource then FEntityDatasource.Tag := Order else inherited; end;
2. 调整EntityData属性的实现逻辑
原SetEntityData的Assign操作会丢失设计时的持久化字段,建议改为只读属性(因为子组件由父组件创建管理),或调整赋值逻辑以保留字段:
// 方案1:改为只读属性(推荐,子组件由父组件内部创建) published property EntityData: TFDQuery read FEntityData; property EntityDatasource: TDatasource read FEntityDatasource; // 方案2:如果必须支持外部赋值,修改SetEntityData逻辑 procedure TKpDBEntityEdit.SetEntityData(const Value: TFDQuery); begin if FEntityData <> Value then begin FEntityData.Assign(Value); // 手动复制持久化字段 FEntityData.Fields.Clear; Value.Fields.AssignTo(FEntityData.Fields); // 通知设计器组件已修改 if csDesigning in ComponentState then (Owner as IDesigner).Modified; end; end;
3. 标记子组件为持久化状态
在创建FEntityData时,添加持久化标记,确保设计器将其属性写入DFM:
constructor TKpDBEntityEdit.Create(AOwner: TComponent); begin inherited Create(AOwner); ... FEntityData := TFDQuery.Create(Self); FEntityData.Name := Self.Name + 'qEntity'; FEntityData.SetSubComponent(True); // 添加此行,标记为持久化组件 FEntityData.ComponentState := FEntityData.ComponentState + [csPersistent]; ... end;
4. 调整ComponentEditor的字段编辑器调用逻辑
确保设计器能正确识别子组件的归属:
procedure TkpEntityEditEditor.ExecuteVerb(Index: Integer); var EntityEdit: TKpDBEntityEdit; begin EntityEdit := TKpDBEntityEdit(Component); case Index of 0: begin // 确保子组件已被设计器注册 if EntityEdit.EntityData.Owner = EntityEdit then Designer.AddComponent(EntityEdit.EntityData); ShowFieldsEditor(Designer, EntityEdit.EntityData, TDSDesigner); end; end; end;
额外检查点
- 若
TKpQuery是TFDQuery的自定义子类,确保其正确继承了父类的持久化字段处理逻辑; - 检查IDE的DFM保存设置,确保启用了"保存子组件属性"选项;
- 添加字段后关闭并重新打开表单,验证DFM文件是否已生成字段的持久化代码。
内容的提问来源于stack exchange,提问作者Marc Guillot
相关产品推荐
相关产品推荐

