【问题标题】:"Object Aware" GUI Controls“对象感知”GUI 控件
【发布时间】:2011-03-01 19:51:31
【问题描述】:

我有一些用 Delphi 编写的业务对象,带有一个自定义的数据库持久性方案,最终可以满足我的需求。好,很好。现在是 GUI 实现的时候了。问题就从这里开始了。

如何将我的对象正确绑定到 GUI?

我无法使用数据感知控件,因为我将所有数据访问组件都隔离到 ORM 层,所以我开始使用 RTTI 单元编写一些“对象感知”控件(我正在使用 Delphi 2010),但我有感觉我走错了路……

关于如何仅使用 VCL 控件解决此问题的一些想法?

【问题讨论】:

    标签: delphi oop user-interface


    【解决方案1】:

    您有几种将 ORM 与用户界面链接的模式。

    例如参见Model GUI Mediator 模式。简而言之,您编写了一个观察者,它将 ORM 内容反映到 UI 组件中,反之亦然。这已在 tiOpf framework for Delphi 中实现(此链接有视频)。

    另一种方法是在运行时映射数据:像往常一样设计表单,然后在 OnShow 事件中填写内容,然后是“保存”或“ OK”按钮将验证然后将内容保存到 ORM 记录中。这是在main Sample application of our framework 中所做的。在这个简单的示例中易于编码,但如果您有很多字段和验证要操作,则可能会导致意大利面条式代码。

    最后一种方法是让你的 ORM 创建表单。

    在我们的框架中,您可以在专用结构中定义有关每个表的一些 UI 属性。然后 a single unit will create a form with all editable fields 你的 ORM 对象。指向其他记录的链接将显示为组合框,布尔值显示为复选框,集合显示为单选框,等等。然后过滤(例如从左侧或右侧的空格中修剪文本字段)和验证(例如确保字段值是唯一的或有效的 IP 地址)处理not in the UI part, but in the business logic itself, i.e. the ORM

    恕我直言,必须保持真正的多层架构。也就是说,UI 必须主要依赖于业务逻辑。例如,数据验证必须是 ORM 的一部分,而不是 UI。例如,如果您决定将 Web 客户端添加到您的 Delphi 客户端应用程序中,则无需再次编写验证代码:这对两个客户端都是通用的,与 UI 实现细节分开。

    【讨论】:

    • 虽然第二种方法在我的特定情况下更容易实现,但我想我会采用第一种,因为从长远来看,它更容易制作和维护复杂的表单。我阅读了链接上的文档,米高梅模式似乎适合我。非常感谢。
    【解决方案2】:

    你可以做的(虽然我没有代码示例)是使用组合

    • 类助手或拦截器类
    • 单个域对象和/或域对象列表的绑定接口

    类助手的缺点是它们不受官方支持,并且您无法将任何字段添加到您正在帮助的类中。

    拦截器类只是与祖先同名的后代类:

    uses
      stdctrls;
    
    type
      TButton = class(stdctrls.TButton)
      end;
    

    您可以将拦截器类放在它们自己的单元中,并在任何您想要的地方使用它。只需确保在标准单元之后包含这些单元,以便在运行时使用您的后代。

    拦截器类的好处:

    • 您可以继续使用标准 VCL 或第三方控件设计您的 UI。
    • 您可以获得后代的所有优势。
    • 您无需创建或安装自己的控件。
    • 不需要特殊的映射器类或使用 RTTI。
    • 按照 Julian Bucknall 在(不同的)Delphi 杂志中关于此问题/答案中提到的文章的内容,轻松(嗯,相对容易)集成到 (DUnit-) 可测试用户界面中:Unit-testing mouse event handlers

    带有绑定接口/命令接口的拦截器控件伪样例:

    uses
      stdctrls;
    
    type
      ICommandAction = interface(IInterface)
        function IsEnabled: Boolean;
        procedure Execute;
        procedure Update;
      end;
    
      IBindSingle = interface(IInterface)
        function GetValueFromControl: string;
        procedure LoadValueIntoControl(const aValue: string);
      end;
    
      TButton = class(stdctrls.TButton, ICommandAction)
      protected
        function IsEnabled: Boolean;
        procedure Execute;
        procedure Update;
      end;
    
      TEdit = class(stdctrls.TEdit, IBindSingle)
        function GetValueFromControl: string;
        procedure LoadValueIntoControl(const aValue: string);
      end;
    

    实施可能是这样的:

      function TButton.IsEnabled: Boolean;
      begin
        Result := Self.Enabled;
      end;
    
      procedure TButton.Execute;
      begin
        Self.Action.Execute;
      end;
    
      procedure TButton.Update;
      begin
        Self.Action.Update;
      end;
    
      function TEdit.GetValueFromControl: string;
      begin
        Result := Self.Text;
      end;
    
      procedure LoadValueIntoControl(const aValue: string);
      begin
        Self.Text := aValue;
      end;
    

    【讨论】:

      【解决方案3】:

      我现在的客户过去(在我来之前)制作了自己的“映射器”类。 它们的数据对象具有字段(即对象),您可以将这些字段映射到控件。我使用类似 MVC 的方法扩展了框架:

      edtTarraCode: TAdvEdit;
      
      procedure TframTarraTab.InitMapping;
      begin
        ...
        Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID);
        ...
      end;
      

      为每个控件创建一个简单的“映射”类:

      TMappingAdvEdit = class(TBaseEditMapping)
      protected
        procedure InitControl; override;
      
        procedure AppData2Control; override;
        procedure Control2AppData; override;
      end;
      

      没有火箭科学,同时可能有更好的解决方案可用(这在 D6 和更低版本中有效 :-))但它对客户来说已经足够好了。

      顺便说一句:还使用了数据对象生成器。因此,如果数据库中的某个字段发生更改(例如 tarra.tarid 更改为 tareID),我们会收到编译器错误,因为“tarid”不再存在。这比“固定字符串”映射(运行时错误)要好得多。

      【讨论】:

      • 这是一个有趣的解决方案,但它需要对我的业务对象进行完全重新设计......它会更复杂,但我想我会尝试 A 建议的 Model GUI Mediator 模式.Bouchwz。非常感谢,伙计。
      【解决方案4】:

      目前无法仅使用 VCL 控件来执行此操作。我知道 Lazarus 有一套基于 RTTI 的数据感知控件;你可能想看看他们的一些基本想法。但这比你一开始想象的要困难。例如,与数据集不同,对象在其成员的值发生变化时没有内置的信号机制。这意味着除非您的数据绑定控件完全拥有该对象并且没有其他任何东西可以访问它,否则一些其他代码可能会更改某些值,然后该更改不会反映在 UI 中。

      在过去的几年里,我从 Delphi 团队听到了各种关于扩展对象模型或 RTTI 模型以实现更好的数据绑定的信息,但无论是什么,都还需要几年的时间。

      【讨论】:

      • 我在 1998 年(Delphi 4)围绕这些原则开发了一个完整的对象感知框架,以及一个等效的数据映射层。但是,正确执行此操作所涉及的工作量比您想象的要多得多 - 给自己 6 到 12 个月的时间来开发可以在非平凡商业应用程序中使用的东西。虽然您可以在一天左右的时间内制作这些东西的原型,但这绝对不能说明正确实施设计所需的工作量。
      【解决方案5】:

      查看http://www.inovativa.com.br 上的 EverClassy 数据集。它可能会满足您的需求。 EverClassy Dataset 是一个 Delphi 数据集,旨在由对象而不是来自数据库系统的记录填充。

      使用此组件,您将有机会将您的域对象与数据件组件进行互操作,这将为您构建 GUI 提供强大的能力。

      【讨论】:

        猜你喜欢
        • 2011-02-21
        • 2016-10-26
        • 1970-01-01
        • 1970-01-01
        • 2020-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-09
        相关资源
        最近更新 更多