(原创) Delphi中LiveBinding 绑定非数据库类数据的时候显示字段自定义名称(二)

DELPHI 10.4版本后,有关LivingBind的源代码修改了,原来的适用于10.3.3以前的方法不适用了,再来分析下10.4版本以后适用的方法。

​​​​​​(原创) Delphi中LiveBinding 绑定非数据库类数据的时候显示字段自定义名称(一)_看那山瞧那水的博客-优快云博客

10.4版本后,部分源代码修改了,删除了接口IScopeMemberDisplayNames,所以原来的方法不能用了。有关显示的部分放到了接口IEditFormatLink里,这个接口声明在System.Classes单元,是个基础接口。(电脑上没有10.3.3以前的版本了,不知道原来有没有这个接口)

通过断断续续的摸索和看源码,总算知道了部分LiveBinding的部分基本流程。其基础是Delphi里早已存在的Observer机制(观察者模式)。我记得在D7里的TObject就有这个Observer。基本版的观察者模式比较简单,但是Delphi的这个就复杂了。支持整个TControl(这里是指FMX框架下)的LiveBinding,单向或双向的绑定。接口众多,文档只说应用,没有更深入的说明,看得是晕头转向。还好只关心如何显示自定义名称,其它先不管(比如绑定表达式等)。

首先先看看是如何绑定数据库并支持显示自定义名称。

绑定数据库的绑定源组件是TBindSourceDB->TCustomBindSourceDB->TBaseLinkingBindSource,就到了基类TBaseLinkingBindSource,TBaseLinkingBindSource是父类TBaseBindScopeComponent的套壳,

TBaseBindScopeComponent = class(TComponent, IScopeComponent, IScopeExpressions, IScopeLocks)

数据库绑定组件是从TCustomBindSourceDB开始的,看看声明:

  TCustomBindSourceDB = class(TBaseLinkingBindSource, IScopeEditLink, IScopeRecordEnumerable, IScopeRecordEnumerableBuffered,
    IScopeNavigator, IScopeActive, IScopeState, IScopeEditor, IScopeMemberNames, IScopeCurrentRecord,
    IScopeMemberScripting, IScopeGetRecord,
    IScopeLookup, IScopeNavigatorUpdates, IScopeBuffer, IScopeLocate, IScopeUnidirectional,
    IScopeRecordControlUpdator)

眼花缭乱了,这里有关显示的接口是 IScopeEditor。

说些题外话:这里当然不能直接看出是和IScopeEditor有关,是不断的调试和摸索,查看我们关心的问题,才知道和这个接口有关,但还只是间接的关系。我们知道数据库字段显示的时候是会显示我们定义的域DisplayName的,FDC是DisplayLabel,我们猜测源码里应当有DisplayName的相关代码,TField有个DisplayName的属性,可以在Data.Bind.DBScope单元里找到相关代码,并且是在IEditFormatLink接口实现里,但是这个接口不在TCustomBindSourceDB 的实现列表里,然后找到IScopeEditor里的方法GetFormatLink()。IScopeEditor的主要功能是管编辑的,IEditFormatLink是管编辑格式的,比如对齐、宽度什么的,然后把显示名称也放在这里了。然后和GRID控件相关的是在Data.Bind.Grid单元里,看到有源码FHeader := LFormatLink.DisplayName; 这里LFormatLink是IEditFormatLink。

回归正题,看看是如何取得TField.DisplayName的。

TCustomBindSourceDB有个字段,FEditFormatLinks: TDictionary<TField, IEditFormatLink>;

其GetFormatLink()方法:

function GetFormatLink(const AFieldName: string): IEditFormatLink;

var
  LField: TField;
begin
  Result := nil;
  if CheckDataSet then
  begin
    LField := FDataSource.DataSet.FindField(AFieldName);
    if Assigned(LField) then
      if not FEditFormatLinks.TryGetValue(LField, Result) then
      begin
        Result := TEditFormatFieldLink.Create(Self, LField) as IEditFormatLink;
        FEditFormatLinks.Add(LField, Result);
      end;
  end;
end;

这里实现了IEditFormatLink的实例,并保存在FEditFormatLinks里。

IEditFormatLink接口的实现:

  TEditFormatFieldLink = class(TInterfacedObject, IEditFormatLink)
  private
    [weak] FOwner: TCustomBindSourceDB;
    FField: TField;
    FCurrency: Boolean;
  protected
    { IEditFormatLink }
    function GetDisplayName: string;
    function GetDisplayWidth: Integer;
    function GetDisplayTextWidth: Integer;
    function GetReadOnly: Boolean;
    function GetVisible: Boolean;
    function GetCurrency: Boolean;
    function GetEditMask: string;
    function GetAlignment: TAlignment;
    function GetMaxLength: Integer;
  public
    constructor Create(AOwner: TCustomBindSourceDB; AField: TField);
  end;

可以看到通过TField可以取得所需信息。

分析源码的时候,还有些其它相关消息,比如Grid是如何自动创建列的并绑定到相应字段,并填充数据内容,设置Grid头(这个就是我们要实现的主要内容)等等。对于GRID这个控件,还有个相关的单元Fmx.Bind.Grid(VCL也有个VCL.Bind.Grid)等等。

明白了我们要实现的核心是如何实现字段的IEditFormatLink接口(数据库是和字段相关,对于一般对象和一般域和属性相关)。注意这里的IEditFormatLink是每个字段一个的。

现在来看看一般对象的绑定。

相关单元Data.Bind.ObjectScope,和TCustomBindSourceDB对应的是TBaseObjectBindSource:

  TBaseObjectBindSource = class(TBaseLinkingBindSource, IScopeEditLink, IScopeRecordEnumerable,
    IScopeNavigator, IScopeState, IScopeEditor, IScopeMemberNames, IScopeCurrentRecord, IScopeActive,
    IScopeMemberScripting, IScopeGetRecord,
    IScopeLookup, IScopeNavigatorUpdates, IScopeLocate,
    IScopeRecordControlUpdator)

我们看到也实现了IScopeEditor接口,也有GetFormatLink()方法,但是这个方法是实现是不同的:

function TBaseObjectBindSource.GetFormatLink(const AFieldName: string): IEditFormatLink;
begin
  Result := nil;
  if CheckAdapter then
    Result := GetInternalAdapte

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值