可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件吗?

时间:2012-02-22 13:54:22

标签: delphi delphi-2009 delphi-xe2

我有一个业务对象,我想更好地“连接”到我的UI。我已经看到了一些使对象具有数据感知能力的部分解决方案,但它们都涉及对业务对象的重大更改,包括额外的抽象层。

我一直在研究新版本Delphi中改进的RTTI,它看起来非常有趣和有用。我想知道我是否可以使用它以编程方式为所有属性注入新的写入方法。

这将起作用的方式是我的TEdit后代将在构建表单时给出对象属性的引用。然后,TEdit将在该属性的属性中插入对自身的引用(当然,在析构函数中删除自身或者给出另一个引用)。 TEdit还将确保在调用原始write方法之后将属性的write方法替换为通知TEdit更改的方法。

这可行吗?最重要的一点就是注入一种新的写入方法是不可能的,因此是这个问题的标题。

派生属性也存在潜在问题,但应该可以找到解决方案。

3 个答案:

答案 0 :(得分:1)

你的问题已经让你在编程技巧方面领先于我,所以我只想补充一下如何处理这个问题:

如果我尝试编写类似的东西,我可能会从TBusinessObject中每个字段的TList开始。该列表将用于指示在您需要推出更改时需要更新的内容。

因此,当创建TEdit时,它会将自己添加到与TBusinessObject中的一段数据相关联的列表中。当TBusinessObject更新该数据时,它将运行附加对象列表。它会看到TEdit,并且知道它是一个TEdit,它将运行代码来更新.Text。如果我附加了TCaption,那么代码将更新.Caption。

正如您所指出的,TEdit需要告知TBusinessObject何时更新其值。我想这是一个棘手的问题 - 你可以创建一个新的TEdit并添加一个TList来维护它应该在它发生变化时应该通知的人。如果您使用.Tag在TBusinessObject中指示字段编号,则OnChange(或任何事件)可以调用类似TBusinessObject.FieldUpdate [TEdit.Tag,NewValue]的内容,然后触发您的业务逻辑。反过来,这可能会使TBusinessObject更新其他字段,这些字段可能有自己的TList来更新字段。

防止循环更新需要您在不触发事件的情况下更新控件。对于我编写的一个程序,我有两种方法来更新控件:SetValue和ChangeValue。 SetValue禁用任何事件(OnChange,OnValidate),更新控件的值,然后重新启用事件。 ChangeValue只是更改了值,并允许任何控件的事件根据需要触发。

可能有更明智的方法可以做到这一点,但希望这给你提供了思考的食物。

答案 1 :(得分:1)

  

可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件吗?

不,这是不可能的。 RTTI为您提供信息,它不能在运行时更改类型。

  

大节目的阻止是注入新的写法不可能,因此这个问题的标题

为了让你在运行时更改它,应该有类似于你可以设置的事件处理程序。这是一个简单的概念,但它有一些运行时开销,无论是在调用时间(它是直接调用通常就足够的间接)和所需的内存(每个属性都需要额外的TEvent样式字段)。如果你需要它,很容易实现,但如果编译器自动为所有类生成这样的代码“以防万一”,这将是有害的。

如果你想在运行时以某种方式修补内存中的代码,那就不会起作用,而且最多也是不可靠的。

答案 2 :(得分:0)

在{{3>}名为诱导大分裂post中,Cobus Kruger讨论了业务对象。

他煮的解决方案基本上符合您的要求:

  1. 利用最近Delphi版本中引入的高级RTTI功能。
  2. 将业务逻辑与表示逻辑分离。
  3. 任何PODO(普通旧Delphi对象)都将作为业务对象!

    魔法放在 TObjectBinding 类中,它将任何 TWinControl 与任何业务对象联系起来。

    摘录:

    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;
    

    我希望这对你来说是一个很好的起点。