zoukankan      html  css  js  c++  java
  • SmartBinding与kbmMW#2

    前言

    在之前的文章中,我介绍了SmartBinding作为Delphi的一个新的易于使用和智能的绑定框架。介绍了包括绑定对象,列表,常规数据和可视控件,以及如何使用导航器,所有这些都用代码做了演示。

    本文将重点关注下一个kbmMW版本中包含的新SmartBinding功能(SmartBinding v2),预计很快就会发布。

    一行代码绑定

    为使kbmMW SmartBind更加智能,目的之一就是要删除所有重复的绑定代码,使开发者只关注具体的功能需求。通过代码执行SmartBinding非常简单,但为了更容易实现,请看下面的演示:

     

    先做一个简单的Form,如上图,Form包含一个TLabel,一个TButton和两个TEdit。

    试着运行它:

    当在Edit3输入内容时,其余控件的Caption和Text都会自动更新。这个功能我已经在之前的SmartBinding博文中介绍过,不知你还记得不?

    在之前的博文中,有许多Binding.Bind 代码行将控件绑定在一起,但在此例子中,只需要在TForm的OnCreate事件中用一行绑定代码:

         Binding.AutoBind(self);

    怎么可能?!现在的kbmMW SmartBinding能够检查对象和控件的String属性,以获取绑定指令。

    Label.Caption属性中可以清楚地看到一个绑定指令:{Edit3.Text}

    只需将绑定内容放在Label.Caption中,Label就会与控件Edit3.Text属性绑定,来接收数据。按钮的绑定方式与Edit3相同。第二个TEdit也绑定到Edit3,但在这里进行了双向绑定:{Edit3.Text,twoway:true}

    总之,你现在可以直观地看到控件从哪里获取可视数据。

    多绑定

    现在我们看看Demo应用程序的第二页:

    这显示了如何在一个String属性中指定多个绑定。TButton控件的Caption属性现在设置了一个包含2个绑定的数组,一个是Edit1.Text绑定到Label2.Caption,另一个是Edit1.Text绑定到Label1.Caption。键入Edit1将按预期更新两个Label。

    你可能已经注意到,因为我们使用按钮的Caption来包含绑定数组,所以按钮看起来不是最终用户友好的。但是,这很容易通过以下方法之一来解决:

    1. 不要使用TButton.Caption进行绑定,而是使用在运行时不可见的其他String属性
    2. 将TButton.Caption绑定到产生您想要所见内容的东西上
    3. 使用值语法

    值语法就象下面这样定义绑定:

    [{value:"Button"},{bind:Edit1.Text, to:Label2.Caption}, { bind: Edit1.Text, to: Label1.Caption}]

    运行后的结果:

    象值语法这样的方式,使用常量字符串作为值,使得Button现在有一个正常的标题了,但也可以使用kbmMW配置框架,具体请参阅有关kbmMW配置管理器的博文

    通过将exprToSrc:“expression”和/或exprToDest:“expression”字符串属性添加到相关绑定部分,也可以指定目标和源表达式通过设置disabled:true,可以将绑定设置为默认禁用

    绑定和数据分离

    如果能够在设置任何真实数据之前设计GUI,那不是很好吗?换句话说,能够开发实时模型,并且可以通过开关来转换为实际应用程序。

    这就是绑定/数据分离发挥作用的地方,现在我们来看Demo的第三页:

    在这个页面上,有8个Label,其中7个看起来像是对它们有特定的意义的设置。

    通过使用@语法,我告诉kbmMW SmartBinding它应该与名为test的数据占位符绑定。

    让我们运行它:

    发生了什么?在实时系统中,您将看到绑定从@test获取数据。@Test.Value2@ test.Value3填充了静态数据,而其余的绑定产生了看似随机的数据。这是来自kbmMW的SmartBinding数据生成器的数据示例。

    在调用AutoBind之前的某个地方,我们必须定义可能正在使用的数据占位符。

    var
       data:IkbmMWBindingData;
    begin
         data:=TkbmMWBindingDataGenerator.Create('{'+
                                              ' value1:10,'+
                                              ' value2:"abc",'+
                                              ' value3:88.4,'+
                                              ' value4: &random {'+
                                              '  type:number,'+
                                              '  random:true,'+
                                              '  min:10000,'+
                                              '  max:100000,'+
                                              '  step:5,'+
                                              '  interval:250'+
                                              ' },'+
                                              ' value5: *random,'+
                                              ' value6: *random,'+
                                              ' value7: *random,'+
                                              ' value8: {'+
                                              '  type: values,'+
                                              '  random: true,'+
                                              '  values: ["AAA","BBB","CCC","DDD","EEE"]'+
                                              ' }'+
                                              '}');
         Binding.DefineData('test',data);
         Binding.AutoBind(self);
    end;

    通过调用DefineData,我们在名称'test'和将生成并接受绑定数据的东西之间建立了一个链接,在这种情况下,绑定是TkbmMWBindingDataGenerator的一个实例data

    它接受一个字符串,其中包含简化的YAML,描述了该生成器应为其生成数据的各种命名属性。每个命名属性可以是常量值,如value1,value2和value3,也可以是描述如何生成特定命名属性的数据的对象。

    看看value4,我们可以看到它是一个对象(它以花括号开头和结尾),并且它包含许多定义数据生成的属性。

    类型目前可以是以下之一:

    • 简单
      这只是静态数据。任何只有与之关联的静态数据的命名属性自动被理解为简单类型。
    • number
      这是一个将返回某种类型的生成器。其他属性定义如何。
    • values
      这是一个返回许多已定义值之一的生成器。其他属性定义如何。

    如果它的编号可以使用这些附加属性:

    • random:true / false 
      如果为true,将生成min和max之间的随机值,舍入到最近的步。
      如果为false,将生成从min开始的增量值(或给定的值),并在每个循环中以步长递增。
    • min:n 
      n是数值。设置此生成器返回的数值的下边界。
    • max:n 
      n是数值。设置此生成器返回的数值的上边界。
    • 步骤:n 
      n是一个数值,表示根据其是随机值还是增量值来完成的步骤或舍入。
    • interval:n 
      n是msecs中的数值,表示值应该更改的频率,interval:250表示它最多每秒更改4次。
    • 小数:n 
      定义后面的小数位数。回来。生成器将生成一个整数值,并将其除以10 ^ n以生成小数。默认值为0。
    • wrap:true / false 
      如果为true且它不是随机值,则生成器将在到达边界时自动回绕。
      如果为false,则在边界处达到的值将变为静态。
    • reverse:true / false 
      如果为true,它将以相反的顺序返回数据。
    • value:x x是要返回的初始值。

    如果是其值,则可以使用以下附加属性:

    • random:true / false 
      如果为true,则会随机选择一个值。
      如果为false则会选择下一个值。
    • 步骤:n 
      n是一个数值,表示根据其是随机值还是增量值来完成的步骤或舍入。
    • wrap:true / false 
      如果为true且不是随机值,则生成器将在达到values数组的边界时自动回绕。
      如果为false,则在边界处达到的值将变为静态。
    • reverse:true / false 
      如果为true,它将以相反的顺序返回数据。
    • interval:n 
      n是msecs中的数值,表示值应该更改的频率,interval:250表示它最多每秒更改4次。
    • values:[...,...,...。] 
      可以为此命名属性返回的值数组。

    kbmMW支持添加其他自定义数据生成器类型。

    我们使用命名属性value4看到的一个特殊语法是YAML锚点(&random)。定义时,其他命名属性值可以使用* random语法引用相同的定义。这就是为什么value5,6和7都返回随机值的原因。

    所以现在我们了解如何定义数据生成器。但是如何在开发过程中使用真实数据替换数据生成器?

    很简单。例如。

    type
      TTest = class
      private
         FVal1:kbmMWNullable<integer>;
         FVal2:kbmMWNullable<string>;
         FVal3:kbmMWNullable<double>;
         FVal4:kbmMWNullable<double>;
         FVal5:kbmMWNullable<double>;
         FVal6:kbmMWNullable<double>;
         FVal7:kbmMWNullable<integer>;
         FVal8:kbmMWNullable<string>;
      public
         constructor Create;
         property Value1:kbmMWNullable<integer> read FVal1 write FVal1;
         property Value2:kbmMWNullable<string> read FVal2 write FVal2;
         property Value3:kbmMWNullable<double> read FVal3 write FVal3;
         property Value4:kbmMWNullable<double> read FVal4 write FVal4;
         property Value5:kbmMWNullable<double> read FVal5 write FVal5;
         property Value6:kbmMWNullable<double> read FVal6 write FVal6;
         property Value7:kbmMWNullable<integer> read FVal7 write FVal7;
         property Value8:kbmMWNullable<string> read FVal8 write FVal8;
      end;
    ...
    constructor TTest.Create;
    begin
         inherited;
         FVal1:=21;
         FVal2:='TESTING';
         FVal3:=11.1;
         FVal4:=22.2;
         FVal5:=33.3;
         FVal6:=44.4;
         FVal7:=55;
         FVal8:='TESTING more!';
    end;
    ...
    begin
         testdata:=TTest.Create;
         Binding.DefineData('test',testdata);
    end;

    调用此代码时,我们会动态地使用对象testdata中的数据替换数据生成器。因此,单击“Redefine test data”按钮将产生下面的运行结果:

    因此,我们现在已经展示了真正的关注点分离,这使得开发者可以独立地设计GUI及控制代码,并且可以作为团队中个人单独任务来开发数据层。

    现在我们来到Demo的最后一页。

    这也是一个绑定分离的演示。
    在这里,我们将对象列表定义为初始绑定数据:

      TLine = class
      private
         FVal1:kbmMWNullable<string>;
         FVal2:kbmMWNullable<integer>;
         FVal3:kbmMWNullable<double>;
      public
         property Val1:kbmMWNullable<string> read FVal1 write FVal1;
         property Val2:kbmMWNullable<integer> read FVal2 write FVal2;
         property Val3:kbmMWNullable<double> read FVal3 write FVal3;
      end;
    
      TLines = TObjectList<TLine>;
    ...
      FLines:=TLines.Create;
    ...
    var
      line1,line2,line3:TLine;
    begin
         Flines:=TLines.Create;
         line1:=TLine.Create;
         line2:=TLine.Create;
         line3:=TLine.Create;
         line1.Val1:='Hej 1';
         line1.Val2:=1;
         line1.Val3:=1.1;
         line2.Val1:='Hej 2';
    //     line2.Val2.IsNull:=true;
         line2.Val3:=2.2;
         line3.Val1:='Hej 3';
         line3.Val2:=3;
         line3.Val3:=3.3;
         Flines.Add(line1);
         Flines.Add(line2);
         Flines.Add(line3);
    
         Binding.DefineData('test2',FLines);
    end;

    我们(在这段代码中为了它的乐趣)将FLines数据绑定到可视控件:

         Binding.Bind(Flines,'Val1',edVal1,'Text',[mwboTwoWay]).Navigator;
         Binding.Bind(Flines,'Val2',edVal2,'Text',[mwboTwoWay]);
         Binding.Bind(Flines,'Val3',edVal3,'Text',[mwboTwoWay]);

    Next和Prev按钮包含以下代码:

    // The Prev buttons eventhandler:
    var
       nav:IkbmMWBindingNavigator;
    begin
         nav:=Binding.GetDataNavigator('test2');
         if nav<>nil then
            nav.Previous;
    end;
    
    // The Next buttons eventhandler:
    var
       nav:IkbmMWBindingNavigator;
    begin
         nav:=Binding.GetDataNavigator('test2');
         if nav<>nil then
            nav.Next;
    end;

    这段代码与我在第一篇博文中所展示的略有不同,因为我没有存储绑定及其导航器以供以后使用,而是根据定义的绑定的名称在需要时请求导航器。数据。

    运行它,它将像我们习惯的那样:

    我们可以使用Prev和Next按钮滚动值。

    然而…。如果我们现在想用数据库中的真实数据替换这个模型列表怎么办?

    对于演示,我们使用内存表的内容模拟数据库中的数据。

         mt:=TkbmMemTable.Create(nil);
         mt.FieldDefs.Add('Val1',ftString,30);
         mt.FieldDefs.Add('Val2',ftInteger);
         mt.FieldDefs.Add('Val3',ftFloat);
         mt.FieldDefs.Add('Val4',ftString,30);
         mt.CreateTable;
         mt.Open;
         mt.AppendRecord(['value 1',1,111.1,'value 1_2']);
         mt.AppendRecord(['value 2',2,222.2,'value 2_2']);
         mt.AppendRecord(['value 3',3,333.3,'value 3_2']);
         mt.AppendRecord(['value 4',4,444.4,'value 4_2']);

    现在我们只需要“切换”数据而不是基于TLines的数据。“重新定义test2数据”的事件处理程序如下所示:

         Binding.DefineData('test2',mt);

    单击它,屏幕将立即如下所示:

    以前链接到FLines实例的所有绑定现在已重新链接到内存表实例,我们可以继续单击Prev和Next来滚动数据。

    还有什么?

    除了上述自动绑定功能,它还支持使用kbmMW中许多其他地方支持的$(configpath)语法从配置框架中获取数据,SmartBinding v2还包括:

      • 支持kbmMWNullable类型
      • GroupedBy(..),NamedBy(..)绑定方法,可用于识别和操作绑定。在基于字符串的自动绑定中,将组作为组提供:“groupname”和名称作为名称:“bindingname”。可以有多个具有相同名称和组的绑定。
      • 通过公共单例BindingGeneratorRegistrations进行自定义绑定数据生成器注册
      • 改进了对焦控制的检测,以便在打字时处理双向绑定,更加优雅。
      • 过程UnbindBindings(const ABindings:TList <IkbmMWBinding>); 
        一次取消绑定绑定列表
      • procedure EnableBindings(const ABindings:TList <IkbmMWBinding>; const AEnable:boolean); 
        一次启用或禁用绑定列表
      • procedure UnbindByGroup(const AGroup:string); 
        取消绑定基于组名的绑定。
      • procedure UnbindByName(const AName:string); 
        取消绑定基于绑定名称的绑定。
      • procedure EnableByGroup(const AGroup:string; const AEnable:boolean); 
        根据组名启用/禁用绑定。
      • procedure EnableByName(const AName:string; const AEnable:boolean); 
        根据绑定名称启用/禁用绑定。
      • function BindingsByGroup(const AGroup:string):TList <IkbmMWBinding>; 
        根据组名返回绑定列表。
      • function BindingsByName(const AName:string):TList <IkbmMWBinding>; 
        根据绑定名称返回绑定列表。

    结束语

    通过轻松支持绑定和数据分离,可以更轻松地完成功能模型(提供服务器和GUI应用程序的数据),只需轻轻一按开关即可将其转换为生产代码。

    它可以以多种方式使用。一种方法是能够通过绑定到来自生成器的简单静态文本集,或者通过创建新的自定义翻译生成器来自动翻译GUI上的所有文本,您可以在其中选择当前语言,因此返回显示以及如何(控件的大小和位置可以以相同的方式绑定)。

    自动绑定将锅炉板编码减少到绝对最小值,同时仍然满足简单重构GUI的要求,只是因为大多数情况下的绑定定义将遵循特定控件,只要在其中一个控件中定义绑定即可字符串属性。在其他控件(可视或非可见)字符串属性中定义绑定的情况下,您至少不会通过简单地重构控件来松散绑定定义。

    它可以轻松访问加载和保存文件的绑定,以处理非常晚的绑定,这可以在安装时用于非常动态的应用程序客户专业化,而不是编译。

  • 相关阅读:
    CSS浮动(float、clear)通俗讲解
    JAVA 类的加载
    数据库操作 delete和truncate的区别
    正则表达式 匹配相同数字
    Oracle EBS OM 取消订单
    Oracle EBS OM 取消订单行
    Oracle EBS OM 已存在的OM订单增加物料
    Oracle EBS OM 创建订单
    Oracle EBS INV 创建物料搬运单头
    Oracle EBS INV 创建物料搬运单
  • 原文地址:https://www.cnblogs.com/kinglandsoft/p/10924982.html
Copyright © 2011-2022 走看看