一步一步学习使用LiveBindings(9) LiveBindings图像绑定与自定义绑定方法(2)

一步一步学习使用LiveBindings(9) LiveBindings图像绑定与自定义绑定方法(2)

这是《一步一步学习使用LiveBindings(8)》的接续,本章将学习如下知识点:

  • 为TGrid应用列格式。
  • 创建自定义的绑定方法。
  • 实现表单级别的格式化方法。

上一节使用向导将TGrid绑定到ProtoTypeBindSource,它会创建TLinkGridToDataSource这个绑定链接。向导创建的绑定只是将PrototypeBindSource1的*与TGrid的*进行了绑定,这种绑定模式在运行时会自动创建Column,所以设计时没有办法去操控列。

绑定的正确方式应该是在PrototypeBindSource1的Field与TGrid的Column进行绑定,

1. Grid的列格式化

如果需要对单独的列进行操作,可以使用两种方法:

  1. 选中LinkGridToDataSourcePrototypeBindSource1,然后在属性编辑器中选择"Columns"属性,单击右侧的按钮,打开列编辑器,在这里手动添加新的列,并设置DataMember为相应的字段,或者单击"Add All Fields"按钮,将一次性添加所有的字段。

  2. 在LiveBindings Designer中将Field直接拖动到TGrid上,完成绑定操作。

在完成后,在LiveBindings Designer选中任意一条连接线,属性面板上均只显示LinkGridToDataSourcePrototypeBindSource1这个绑定链接,因此如果要添加公式,需要在属性面板中,打开Columns编辑器,选中某一个具体的Column进行设置。

每一个Column具有如下的一些属性:

  • ColumnStyle : 默认情况下,列被创建为 TStringColumn 。但是,您可能希望选择一个与其内容相关的更好值(数字、日期值等)。
  • CustomFormat : 此属性允许您指定用于在网格中渲染值的表达式。
  • CustomParse : 此属性允许您指定用于将用户输入的值转换为列的适当值的表达式。
  • Header : 该属性设置列的标题。
  • MemberName : 该属性设置列的对应字段。
  • ReadOnly : 该属性定义列是否可以被用户编辑。
  • Visible : 该属性设置列的可见性(你可以创建列,然后决定每列的可见性)。
  • Width : 该属性指定列的宽度。
    CustomFormat和CustomParse格式表达式,与本系列在《一步一步学习使用LiveBindings(8)》中介绍的使用方式完全相同。
    下图是接下来实现的效果:

这里为HireDate列应用了日期格式化,

CustomFormat:

Pascal 复制代码
FormatDateTime('MMMM yyyy',self.Value)

Salary列应用了货币格式化。

CustomFormat:

Pascal 复制代码
Format('%%m', self.Value + 0.0)

CustomParse:

Pascal 复制代码
SubString(%s, 1, 15)

在Grid最后添加了2列,一列是薪资等级,如果薪资高的,就加了一个绿色向上的手势,如果薪资不是那么高的,就加了一个绿色的手势。看上去就显得专业多了。

要实现这样的目的,在这里添加了2个新的列,一个列的ColumnStyle指定为GlyphColumn,另一个列指定为ProgressColumn列类型。

注意:虽然属性编辑器中,ColumnStyle下拉框并没有提供一个ProgressColumn类型,但仍然可以通过输入ProgressColumn来显示进度条。

GlyphColumn可以显示来自TImageList控件中的图片,因此在主窗体上添加了一个TImageList控件,将TGrid的Images属性指向它。

在这个ImageList控件中,仅放了2张图片,分别是2个手势,因此其ImageIndex分别为0和1。在CustomFormat中,写下如下的公式:

Pascal 复制代码
IfThen(Self.Value>60000, 0, 1)

如果年薪大于6万,则认为是高薪了,打个绿色的手势,否则显示红色的手势。

ProgressColumn将使用表达式给它一个当前最高薪资的比率。如果是Manager,则最高薪资为20万,否则最高薪资设为10万。公式如下:

Pascal 复制代码
100*(Self.Value /IfThen(Owner.Title.Value='Manager', 200000, 100000) )

可以看到在表达式中又套用了表达式,这个可以灵活应用。

2. 自定义绑定方法

货币格式目前的解析公式不能满足需求,SubString(%s, 1, 15)虽然简单的移除了前缀的货币符号,但是对于数字中的分隔符号就不能识别,因此会发生错误。系统内置的方法并没有好的解决方案,好在还可以创建自定义的绑定方法。

请按下面的步骤实现自定义的绑定方法:

1. 单击主菜单中的 File > New > Package ,创建一个新的Delphi包。

建议立即单击工具栏上的Save All按钮,将包保存为CurrencyBindingMethod.dproj。

然后在包中添加一个新的Unit,保存为Methods.FormatCurrency.pas。

创建自定义绑定方法需要调用System.Bindings.Methods单元中的MakeInvokable方法,它返回定义在System.Bindings.EvalProtocol单元中的IInvokable接口,需要在uses区中添加对这2个单元的引用。

完整代码如下所示,请参考代码步骤:

Pascal 复制代码
unit Methods.FormatCurrency;

interface

uses
  SysUtils;

implementation

uses
  //MakeInvokable 函数、 TMethodDescription 类型定义和 TBindingsMethodFactory 单例
  System.Bindings.Methods,
  //IValue,TValueWrapper 和 IInvokable 类型定义
  System.Bindings.EvalProtocol;

//将货币类型专换为字符串类型
function CurrencyToStrInvokable: IInvokable;
begin
  Result := MakeInvokable(
    function (Args: TArray<IValue>): IValue
    var
       AValue:Currency;
       CurrencyStr:string;
    begin
      // 1 - 输入参数识别并进行验证
      if Length(Args) < 1 then
        raise EEvaluatorError.Create('CurrencyToStr: 至少需要一个参数');
      // 2 - 调用真实的转换CurrToStrF方法
        AValue:=Args[0].GetValue.AsCurrency;
        CurrencyStr:= CurrToStrF(AValue, ffCurrency, 2);
      // 3 - 将结果值打包为TValueWrapper
      Exit(TValueWrapper.Create(CurrencyStr));
    end
  );
end;

//将字符串类型专换为货币类型。
function StrToCurrencyInvokable: IInvokable;
begin
  Result := MakeInvokable(
    function (Args: TArray<IValue>): IValue
    var
       AValue:String;
       CurrencyVal:Currency;
       C: char;
       LDigits: string;
    begin
      // 1 - 输入参数识别并进行验证
      if Length(Args) < 1 then
        raise EEvaluatorError.Create('StrToCurrency: 至少需要一个参数');
      // 2 - 将参数转换为真实的参数类型,再进行实际的业务处理
        AValue:=Args[0].GetValue.AsString;
        for C in AValue do
          case C of
           '0'..'9',
           '.':
             LDigits := LDigits + C;
          end;
      CurrencyVal := StrToCurr(LDigits);
      // 3 - 将结果装包装起来
      Exit(TValueWrapper.Create(CurrencyVal));
    end
  );
end;

initialization
  // 4 -  注册方法
  TBindingMethodsFactory.RegisterMethod(
    TMethodDescription.Create(
        CurrencyToStrInvokable
      , 'CurrencyToStr', 'CurrencyToStr', '', True
      , '货币转换为字符串'
      , nil
    )
  );

  TBindingMethodsFactory.RegisterMethod(
    TMethodDescription.Create(
        StrToCurrencyInvokable
      , 'StrToCurrency', 'StrToCurrency', '', True
      , '字符串转换为货币'
      , nil
    )
  );

finalization
// 5 - 卸载已经注册的方法
  TBindingMethodsFactory.UnRegisterMethod('CurrencyToStr');
  TBindingMethodsFactory.UnRegisterMethod('StrToCurrency');

end.

可以看到,代码中定义了CurrencyToStr和StrToCurrency这两个方法,它们都接收一个参数,并且调用了SysUtil中的CurrToStrF和StrToCurr函数进行了转换。

2. 在Project Manager窗口右击CurrencyBindingMethod项目名称,先Build一次,再单击"Install"菜单项进行安装。

安装完成后,回到LiveBindings_BindFormat.dproj项目,选中TBindingList控件,在属性编辑器中找到Methods属性,单击右侧的按钮,可以看到CurrencyToStr和StrToCurrency这两个方法已经出现在了列表中。

3. 要让我们自己的项目能够正确识别这2个方法,还需要将包中的Methods.FormatCurrency.pas单元添加到uMainForm.pas的uses区,否则会提示方法无法找到的错误。

请在Project > Options > Delphi Compiler > Search Path路径中添加了对Package代码的引用。

在uses区中添加如下的引用:

Pascal 复制代码
implementation

{$R *.fmx}

uses
  System.Bindings.Helper, System.Bindings.Methods,
    System.Bindings.EvalProtocol,Methods.FormatCurrency;

将Salary的公式改为如下:

CustomFormat:

Pascal 复制代码
CurrencyToStr(self.Value)

CustomParse

Pascal 复制代码
StrToCurrency(%s)

经过这样的处理,现在在货币的显示和输入上,就非常理想了,如下图所示:

4. 使用自定义的格式化方法

在《一步一步学习使用LiveBindings(8)》中曾经介绍过通过self.owner.owner,返回到了主窗体TMainForm,我们在主窗体上定义了一个属性MyProgName,可以通过self.owner.owner.MyProgName访问到此属性。同理,还可以在主窗体上定义一个函数,通过self.owner.owner进行访问,实现自定义的格式化方法。

实现过程如下:

1. 首先在uMainForm的public区定义了一个函数,实现根据指定的格式化字符串格式化数字,定义与实现如下所示:

Pascal 复制代码
  public
    { Public declarations }
    property MyProgName:string read GetProgName;

    //根据指定的格式化字符串或区域格式化符点数。
    function FormFormatFloat(const AFormat: string; const AValue: Extended; const ALocale: string): string;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.fmx}

uses
  System.Bindings.Helper, System.Bindings.Methods,
    System.Bindings.EvalProtocol,Methods.FormatCurrency;

function TfrmMain.FormFormatFloat(const AFormat: string; const AValue: Extended;
  const ALocale: string): string;
var
  LFormatSettings: TFormatSettings;
begin
  LFormatSettings := FormatSettings;
  if not ALocale.IsEmpty then
    LFormatSettings := TFormatSettings.Create(ALocale);
  Result := FormatFloat(AFormat, AValue, LFormatSettings);
end;

接下来,我使用LiveBindings Wizard向导新建了一个TEdit指向Salary的链接,然后为这个TEdit的绑定链接指定CustomFormat如下,使其保留4位小数:

Pascal 复制代码
Self.Owner.Owner.FormFormatFloat('#,#0.0000', self.Value, '') 

效果如下:

可以看到,果然正确的实现了格式化显示。

右边的那个手势图标,是一个TGlyph组件,它可以连接到一个TImageList控件,通过在LiveBindings Designer中,将Salary绑定到其ImageIndex属性。最后应用了如下的公式:

Pascal 复制代码
IfThen(Self.Value>60000, 0, 1)

总结

本文介绍了格式化的几种进阶方法:

  • 使用表达式列格式化。
  • 自定义绑定方法。
  • 使用自定义表单方法格式化。

强烈推荐使用自定义绑定方法,不是很推荐自定义表单方法,但有时候表单级别的方法能解决很多业务逻辑相关的格式化问题。所以具体如何架构,还是要具体问题,具体分析。

相关推荐
BillKu20 天前
在 Delphi 5 中获取 Word 文档页数的方法
word·delphi
BillKu23 天前
Delphi 5 中操作 Word 表格时禁用鼠标交互
word·delphi
看那山瞧那水24 天前
DELPHI 利用OpenSSL实现加解密,证书(X.509)等功能
delphi·openssl
lincats1 个月前
一步一步学习使用FireMonkey动画(6) 用实例理解动画的运行状态
ide·delphi·livebindings·delphi 12.3·firemonkey
lincats1 个月前
一步一步学习使用FireMonkey动画(5) 动画图解11种动画插值类型
ide·移动开发·delphi 12.3·firedac·firemonkey
lincats1 个月前
一步一步学习使用FireMonkey动画(4) 使用Delphi的基本动画组件类,路径和位图列表动画 弹跳小球和奔跑的小人示例
livebindings·delphi 12.3·firemonkey
lincats1 个月前
一步一步学习使用FireMonkey动画(3) 使用Delphi的基本动画组件类
ide·delphi·delphi 12.3·firemonkey
lincats1 个月前
一步一步学习使用FireMonkey动画(2) 使用TAnimator类创建动画
ide·delphi 12.3·firedac·firemonkey
lincats1 个月前
一步一步学习使用FireMonkey动画(1) 使用动画组件为窗体添加动态效果
android·ide·delphi·livebindings·delphi 12.3·firemonkey
lincats1 个月前
一步一步学习使用LiveBindings(16)使用代码创建LiveBindings绑定
delphi·livebindings·delphi 12.3·firedac·firemonkey