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

发布于 2025-01-08 05:21:41 字数 398 浏览 2 评论 0原文

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

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

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

这可行吗?最大的阻碍是注入新的写入方法是不可能的,因此是这个问题的标题。

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

I have a business object that I would like to "connect" to my UI better. I've seen some partial solutions for making objects data-aware, but they all involved significant changes to my business object, including an extra layer of abstraction.

I've been looking into the improved RTTI in new versions of Delphi, and it looks very interesting and useful. I'm wondering if I could use it to programmatically inject new write methods for all properties.

The way this would work is that my TEdit descendant would by given a reference to an object property when the form is built. The TEdit would then insert a reference to itself in an attribute for that property (and of course remove itself on destructor or being given another reference). The TEdit would also ensure that the write method for the property is replaced by one that notifies the TEdit of changes after calling the original write method.

Is this feasible? The big show stopper would be that injecting a new write method isn't possible, hence the title for this question.

There are also potential problems with derived properties, but it should be possible to find a solution for that.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ぶ宁プ宁ぶ 2025-01-15 05:21:41

您的问题已经使您的编程技能领先于我,因此我将添加如何处理此问题:

如果我要尝试编写类似的内容,我可能会从 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 只是更改值并允许根据需要触发任何控件的事件。

可能有更巧妙的方法可以做到这一点,但希望这能给您带来思考。

Your question already puts you ahead of me with programming skill so I'll just add how I might approach this:

If I were to try to write something like that I'd probably start with a TList for each field in your TBusinessObject. That list would be used to indicate what needed to be updated when you needed to push out a change.

So when the TEdit is created it would add itself to a list which was associated with a piece of data in your TBusinessObject. When the TBusinessObject updated that piece of data it would then run through the list of attached objects. It would see the TEdit and, knowing it was a TEdit, would run code to update the .Text. If I attached a TCaption then the code would update the .Caption.

The TEdit, as you indicated, would need to tell the TBusinessObject when it's value was updated. I guess this is the tricky spot - you could create a new TEdit and add in a TList to maintain who it should inform when it changes. If you used the .Tag to indicate a field number in the TBusinessObject then the OnChange (or whatever event) could then call something like TBusinessObject.FieldUpdate[TEdit.Tag, NewValue] which then triggers your business logic. That, in turn, might make the TBusinessObject update other fields, which may have their own TLists to fields to update.

Preventing circular updates would require that you have a way of updating a control without triggering events. For one program I wrote I had two methods to update the control: SetValue and ChangeValue. SetValue disabled any events (OnChange, OnValidate), updated the control's value and then reenabled the events. ChangeValue simply changed the value and allowed any of the control's events to fire as required.

There are probably slicker ways to do this but hopefully this gives you food for thought.

辞慾 2025-01-15 05:21:41

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

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

最大的阻碍是注入新的写入方法是不可能的,因此这个问题的标题

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

如果您正在考虑在运行时以某种方式修补内存中的代码,那么这是行不通的,而且充其量也是不可靠的。

Possible to change property write methods programmatically using RTTI for creating object-aware controls?

No, it's not possible. RTTI gives you information, it doesn't give the ability to alter types at runtime.

The big show stopper would be that injecting a new write method isn't possible, hence the title for this question

In order for you to change this at runtime there should be something similar to an event handler that you can set. It's an easy concept, but it has some runtime overhead, both in call time (it would be an indirection where a direct call would normally suffice) and in terms of required memory (each property would require an extra TEvent style field). This is easy for you to implement if you need it, but it would be harmful if the compiler automatically generated such code for all classes "just in case".

If you're thinking of somehow patching the code in memory at runtime, that's not going to work and it would be, at best, unreliable.

寂寞花火° 2025-01-15 05:21:41

在这篇题为诱导Great DivideCobus Kruger 谈论了业务对象。

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

  1. 利用最新 Delphi 版本中引入的高级 RTTI 功能。
  2. 将业务逻辑与表示逻辑分离。

任何 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;

我希望这对您来说是一个良好的起点。

In this post entitled Inducing The Great Divide, Cobus Kruger talked about business objects.

The solution he cooked is essentially compliant to your requirements:

  1. Make use of advanced RTTI features introduced in recent Delphi version.
  2. Separation of the business logic from presentation logic.

Any PODO (Plain Old Delphi Object) will do as business object !

The magic lays in the TObjectBinding class which ties any TWinControl with any business object.

Excerpt:

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;

I hope that this will be a good starting point for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文