我有一些使用Delphi自定义数据库持久性方案编写的业务对象,最终可以满足我的需求。太好了。现在是GUI实现的时候了。在这里开始的问题。“Object Aware”GUI控件
如何正确地将我的对象绑定到GUI?
我无法使用数据感知控件,因为我将所有数据访问组件都分离到了ORM层中,因此我开始使用RTTI单元编写一些“Object Aware”控件(我正在使用Delphi 2010),但我有感觉我走错了路...
有关如何解决这个问题的一些想法,只使用VCL控件?
我有一些使用Delphi自定义数据库持久性方案编写的业务对象,最终可以满足我的需求。太好了。现在是GUI实现的时候了。在这里开始的问题。“Object Aware”GUI控件
如何正确地将我的对象绑定到GUI?
我无法使用数据感知控件,因为我将所有数据访问组件都分离到了ORM层中,因此我开始使用RTTI单元编写一些“Object Aware”控件(我正在使用Delphi 2010),但我有感觉我走错了路...
有关如何解决这个问题的一些想法,只使用VCL控件?
您有几种链接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实现细节分开。
目前没有办法只使用VCL控件来做到这一点。我知道Lazarus有一套基于RTTI的数据感知控件;你可能想看看他们的一些基本想法。但起初比你想象的要困难得多。例如,与数据集不同,对象在其成员值发生更改时没有内置的信号机制。这意味着,除非你的数据绑定控件完全拥有这个对象,并且没有其他人可以访问它,否则其他代码可能会改变某个值,然后这个改变就不会反映在UI中。
在过去的几年里,我听说过Delphi团队的各种事情,关于扩展对象模型或RTTI模型以实现更好的数据绑定,但无论如何还需要几年时间。
我在1998年围绕这些原则开发了一个完整的对象感知框架(Delphi 4),以及一个等效的数据映射层。然而,正确地完成这项工作的数量远远超出了您的想象 - 给自己6-12个月的时间来开发一些可以在非平凡的商业应用中使用的东西。虽然你可以在一天左右的时间内对这些东西进行原型设计,但这绝对没有说明正确实施设计所需的工作量。 – Misha 2011-03-01 22:28:49
你能做什么(虽然我没有代码样本)是使用
类助手的缺点是他们没有得到官方的支持,你不能在你正在帮助的类中添加任何字段。
拦截器类是简单的子类具有相同的名称作为自己的祖先:
uses
stdctrls;
type
TButton = class(stdctrls.TButton)
end;
你可以把拦截器类在自己的单位和使用等。无论你想要的。只要确保这些单元包含在标准单元之后,那么您的后代就可以在运行时使用。
拦截器类的好处:
与结合界面/命令接口拦截控制的伪样品:
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;
我现在的客户在过去(我来之前)做过他们自己的“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”不再存在。这比“固定字符串”映射(运行时错误)好得多。
这是一个有趣的解决方案,但它需要对我的业务对象进行完全重新设计......它会更加复杂,但我认为我会尝试按A. Bouchwz所建议的模型GUI介体模式。许多泰克人,男人。 – 2011-03-01 21:01:17
查看EverClassy数据集http://www.inovativa.com.br。它可能会满足你的需求。 EverClassy数据集是一个Delphi数据集,用于填充数据库系统中的对象而不是记录。
有了这个组件,您将有机会将您的域对象与数据软件组件进行互操作,这将为您提供构建GUI的强大功能。
尽管第二种方法在我的特殊情况下可以更容易实现,但我认为我会采取第一种方法,因为从长远角度来看,制作和维护复杂表单会更容易。我阅读链接上的文档,MGM模式似乎适合作为我的手套。非常感谢。 – 2011-03-01 21:25:09