2011-03-01 34 views
8

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

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

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

有关如何解决这个问题的一些想法,只使用VCL控件?

回答

6

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

参见例如Model GUI Mediator模式。总之,你写了一个观察者,它将ORM内容反映到UI组件中,反之亦然。这已经在例如tiOpf framework for Delphi(该链接具有视频)中实施。

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

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

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

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

+0

尽管第二种方法在我的特殊情况下可以更容易实现,但我认为我会采取第一种方法,因为从长远角度来看,制作和维护复杂表单会更容易。我阅读链接上的文档,MGM模式似乎适合作为我的手套。非常感谢。 – 2011-03-01 21:25:09

0

目前没有办法只使用VCL控件来做到这一点。我知道Lazarus有一套基于RTTI的数据感知控件;你可能想看看他们的一些基本想法。但起初比你想象的要困难得多。例如,与数据集不同,对象在其成员值发生更改时没有内置的信号机制。这意味着,除非你的数据绑定控件完全拥有这个对象,并且没有其他人可以访问它,否则其他代码可能会改变某个值,然后这个改变就不会反映在UI中。

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

+2

我在1998年围绕这些原则开发了一个完整的对象感知框架(Delphi 4),以及一个等效的数据映射层。然而,正确地完成这项工作的数量远远超出了您的想象 - 给自己6-12个月的时间来开发一些可以在非平凡的商业应用中使用的东西。虽然你可以在一天左右的时间内对这些东西进行原型设计,但这绝对没有说明正确实施设计所需的工作量。 – Misha 2011-03-01 22:28:49

2

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

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

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

拦截器类是简单的子类具有相同的名称作为自己的祖先:

uses 
    stdctrls; 

type 
    TButton = class(stdctrls.TButton) 
    end; 

你可以把拦截器类在自己的单位和使用等。无论你想要的。只要确保这些单元包含在标准单元之后,那么您的后代就可以在运行时使用。

拦截器类的好处:

  • 您可以继续使用设计标准的VCL或第三方控件的UI。
  • 你得到了后代的所有优点。
  • 您不需要创建或安装自己的控件。
  • 不需要特殊的映射器类或使用RTTI。
  • 轻松(当然,相对容易地)集成到一起儒略巴克纳尔的在(不同)的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; 
1

我现在的客户在过去(我来之前)做过他们自己的“mapper”类。 他们的数据对象具有字段(它们是对象),您可以将这些字段映射到控件。

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; 

没有火箭sience,也许更好的解决方案在平均可我使用MVC式的方法扩展框架时间(这在D6和更低的工作:-)),但它对客户足够好。

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

+0

这是一个有趣的解决方案,但它需要对我的业务对象进行完全重新设计......它会更加复杂,但我认为我会尝试按A. Bouchwz所建议的模型GUI介体模式。许多泰克人,男人。 – 2011-03-01 21:01:17

0

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

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