“对象感知”GUI控件

时间:2011-03-01 19:51:32

标签: delphi oop user-interface

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

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

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

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

5 个答案:

答案 0 :(得分:6)

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

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

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

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

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

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

答案 1 :(得分:2)

你能做什么(虽然我没有代码样本)是使用

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

班级帮助者的缺点是他们没有得到官方支持,你不能在你正在帮助的班级中添加任何字段。

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

uses
  stdctrls;

type
  TButton = class(stdctrls.TButton)
  end;

您可以将拦截器类放在他们自己的单元中,并根据需要使用它。只需确保这些单位包含在标准单位之后,因此您的后代在运行时使用。

拦截器类的好处:

  • 您可以继续使用标准VCL或第三方控件设计UI。
  • 你获得了后代的所有优势。
  • 您无需创建或安装自己的控件。
  • 无需特殊的映射器类或使用RTTI。
  • 根据朱利安·巴克纳尔在(不同的)德尔福杂志中关于此问题的文章中容易(好,相对容易)集成到(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;

答案 2 :(得分:1)

我现在的客户过去曾经做过自己的“映射器”课程(在我来之前)。 它们的数据对象具有字段(对象),您可以将这些字段映射到控件。我通过使用类似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”不再存在。这比“固定字符串”映射(运行时错误)要好得多。

答案 3 :(得分:0)

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

在过去的几年里,我听说过Delphi团队关于扩展对象模型或RTTI模型以实现更好的数据绑定的各种事情,但无论如何还是几年之后。

答案 4 :(得分:0)

http://www.inovativa.com.br查看EverClassy Dataset。它可能满足您的需求。 EverClassy Dataset是一个Delphi数据集,设计用于填充对象而不是数据库系统中的记录。

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