Delphi 是否可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件?
我有一个业务对象,我想更好地“连接”到我的UI。我已经看到了一些使对象具有数据意识的部分解决方案,但它们都涉及对我的业务对象的重大更改,包括额外的抽象层 我一直在研究Delphi新版本中改进的RTTI,它看起来非常有趣和有用。我想知道是否可以使用它以编程方式为所有属性注入新的写方法 这样做的方式是,在构建表单时,我的TEdit子代将通过引用对象属性来实现。然后,TEdit将在该属性的属性中插入对自身的引用(当然,在析构函数上删除自身,或者获得另一个引用)。TEdit还将确保将属性的写入方法替换为在调用原始写入方法后通知TEdit更改的方法 这是否可行?最大的障碍是注入一个新的写方法是不可能的,因此这个问题的标题是Delphi 是否可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件?,delphi,delphi-2009,delphi-xe2,Delphi,Delphi 2009,Delphi Xe2,我有一个业务对象,我想更好地“连接”到我的UI。我已经看到了一些使对象具有数据意识的部分解决方案,但它们都涉及对我的业务对象的重大更改,包括额外的抽象层 我一直在研究Delphi新版本中改进的RTTI,它看起来非常有趣和有用。我想知道是否可以使用它以编程方式为所有属性注入新的写方法 这样做的方式是,在构建表单时,我的TEdit子代将通过引用对象属性来实现。然后,TEdit将在该属性的属性中插入对自身的引用(当然,在析构函数上删除自身,或者获得另一个引用)。TEdit还将确保将属性的写入方法替换为
派生属性也存在潜在问题,但应该有可能找到解决方案。您的问题已经让您在编程技能方面走在了我的前面,因此我只想补充一下如何解决这个问题: 如果我尝试写类似的东西,我可能会从TBusinessObject中每个字段的TList开始。该列表将用于指示当您需要推出更改时需要更新的内容 因此,当创建TEdit时,它会将自己添加到与您的TBusinessObject中的一段数据关联的列表中。当TBusinessObject更新该数据段时,它将在附加对象列表中运行。它会看到TEdit,知道它是TEdit,就会运行代码来更新.Text。如果我附加了一个t选项,那么代码将更新.Caption 正如您所指出的,TEdit需要在TBusinessObject的值更新时通知它。我想这是一个棘手的问题——你可以创建一个新的TEdit,并添加一个TList来维护它在发生变化时应该通知谁。如果您使用.Tag来指示TBusinessObject中的字段号,那么OnChange(或任何事件)可以调用类似TBusinessObject.FieldUpdate[TEdit.Tag,NewValue]的内容,然后触发您的业务逻辑。这反过来可能会使TBusinessObject更新其他字段,这些字段可能有自己的TList到要更新的字段 要防止循环更新,需要有一种在不触发事件的情况下更新控件的方法。对于我编写的一个程序,我有两种方法来更新控件:SetValue和ChangeValue。SetValue禁用任何事件(OnChange、OnValidate),更新控件的值,然后重新启用事件。ChangeValue只是更改了该值,并允许根据需要触发控件的任何事件 也许有更巧妙的方法可以做到这一点,但希望这能让你深思 是否可以使用RTTI以编程方式更改属性写入方法来创建对象感知控件 不,不可能。RTTI为您提供信息,但不提供在运行时更改类型的功能 最大的障碍是注入一个新的写方法是不可能的,因此这个问题的标题是 为了让您在运行时改变这一点,应该有类似于事件处理程序的东西,您可以设置它。这是一个简单的概念,但它有一些运行时开销,包括调用时间(直接调用通常就足够了)和所需内存(每个属性都需要额外的
TEvent
样式字段)。如果需要,这很容易实现,但如果编译器“以防万一”自动为所有类生成这样的代码,这将是有害的
如果您想在运行时以某种方式修补内存中的代码,那么这是行不通的,而且充其量也不可靠。在这篇题为《诱导巨大鸿沟》的文章中,谈到了业务对象
他制作的解决方案基本上符合您的要求:
TObjectBinding = class
private
fCtx: TRttiContext;
fControlType: TRttiType;
fObjType: TRttiType;
fPropFieldMapping: TDictionary<TRttiProperty, TRttiField>; // Dictionary of object Properties & corresponding Fields
fControl: TWinControl; // The control (normally form)
fObj: TObject; // Object it represents.
procedure CreateMappings;
function FindField(Prop: TRttiProperty; out Field: TRttiField): Boolean;
function FieldClass(Field: TRttiField): TClass;
// Modify these to change the rules about what should be matched.
function IsValidField(Field: TRttiField): Boolean;
function IsValidProp(Prop: TRttiProperty): Boolean;
// Modify these to change the mappings of property type to VCL control class.
procedure AssignField(Prop: TRttiProperty; Field: TRttiField);
procedure AssignProp(Prop: TRttiProperty; Field: TRttiField);
// Used from AssignField/AssignProp. Extend these to support a wider range of properties.
function GetPropText(Prop: TRttiProperty): string;
procedure SetPropText(Prop: TRttiProperty; const Text: string);
public
constructor Create(Control: TWinControl; Obj: TObject);
destructor Destroy; override;
//
procedure Load;
procedure Save;
end;
TObjectBinding=class
私有的
fCtx:trtti上下文;
fControlType:TRttiType;
fObjType:trtti型;
fPropFieldMapping:TDictionary;//对象属性和相应字段的字典
fControl:TWinControl;//控件(通常为窗体)
fObj:TObject;//它所代表的对象。
程序映射;
函数FindField(Prop:TRttiProperty;out字段:TRttiField):布尔值;
功能字段类(字段:TRttiField):TClass;
//修改这些以更改有关应匹配内容的规则。
函数IsValidField(字段:TRttiField):布尔值;
函数IsValidProp(Prop:TRttiProperty):布尔值;
//修改这些以更改属性类型到VCL控制类的映射。
过程赋值字段(属性:TRttiProperty;字段:TRttiField);
过程赋值属性(属性:TRttiProperty;字段:TRttiField);
//从AssignField/AssignProp使用。扩展这些功能以支持更广泛的属性。
函数GetPropText(Prop:TRttiProperty):字符串;
过程SetPropText(Prop:trtti属性;const Text:string);
公众的
构造函数创建(控件:TWinControl;对象:TObject);
毁灭者毁灭;推翻
//
程序负载;
程序保存;
结束;
我希望这将是一个很好的起点。也许是一个起点?听起来像宾迪