SmartBinding与kbmMW#1

 

即将发布的kbmMW,实现了SmartBinding,SmartBinding的设计目标是:正则表达式

  • 必须易于使用
  • 必须最小化或彻底删除重复的代码。
    (你看到这里的趋势了吗?... kbmMW初心就是为了让事情变得简单!)
  • 必须有良好的表现
  • 必须具备较低的CPU占用及内存占用
  • 毫不能致使无休止的循环调用
  • 应该使用各类数据和控件
  • 应该是灵活和可扩展的
  • 必须接近实时
  • 必须易于重构
  • 应该与kbmMW的其余功能很好地配合
  • 即便不使用kbmMW的其余功能也应该可用

看一个简单的属性绑定:安全

简单绑定

 

 

     Binding.Bind(Edit1,'Text',Label1,'Caption');
     Binding.Bind(Edit1,'Text',Button1,'Caption');
     Binding.Bind(Edit1,'Text',Edit2,'Text',[mwboTwoWay]);

上面代码,在edit1中输入内容,Label1.Caption,Button1.Caption,Edit2.Text 三个组件显示的内容会跟edit1.text如出一辙。第三个是双向绑定,edit2中输入内容,edit1也会更新。app

Binding实例来自哪里?他是kbmMW SmartBinding默认带的一个实例,能够当即使用。代码的最后一行还包括一个可选标志,表示绑定是两种方式,更改一方也会自动更新另外一方。框架

基本上全部字符串布尔值浮点int64整数属性均可以经过自动转换轻松绑定,其中SmartBinding确保根据须要自动在不一样类型之间转换数据。其余类型的数据也能够绑定,但源和目标属性必须是相同的类型(也有方法也能够...见后面)。dom

线程安全方面是什么状况呢?kbmMW SmartBinding自动识别从TControl类的继承的组件,它必须在主应用程序线程进行更新才保证应用程序稳定运行。函数

接下来,咱们看看Record的绑定:工具

绑定Record

kbmMW SmartBinding实现绑定到常规对象或者Record,只要确保数据始终可用,那绑定就存在。若是数据不存在了,可使用kbmMW SmartBinding实现的解绑与从新绑定功能。下面的代码,实现TEdit与全局Record的绑定:测试

type
  TData = record
    FData1:string;
  end;

var
  data:TData;
...
     Binding.Bind(Edit1,'Text',@data,TypeInfo(TData),'FData1');
     Binding.Bind(@data,TypeInfo(TData),'FData1',EditN,'Text');

如今,Edit1的全部更改会自动填充到数据记录中的FData1字段,一样,对数据记录的FData1字段的全部更改都会自动显示在EditN的Text属性中。编码

线程安全是什么状况?spa

上面的例子显然有些简单。可是,因为两个绑定都引用了TControl子类,所以数据记录的轮询和更新须要在主应用程序/ GUI线程中完成,所以,除非您有另外一个访问data.FData1的线程,不然这将安全地工做。

若是其余线程中修改记录的FData1字段,那你必须进行常规的线程数据锁定。能够用TkbmMWLock这个工具来帮助解决这个问题。

kbmMW SimpleBinding固然还支持任何组合中的对象实例做为数据源或者接收者。

Record能够绑定了,接下来,再看看

绑定对象列表

下面示例说明了如何绑定对象列表。首先让咱们声明一个包含一些数据的列表。

type
  TLine = class
  private
     FName:string;
     FAddress:string;
  public
     constructor Create(const AName:string; const AAddress:string);
     property Name:string read FName write FName;
     property Address:string read FAddress write FAddress;
  end;

  TLines = TObjectList<TLine>;

var
   lines:TLines;
...
     lines:=TLines.Create;
     lines.Add(TLine.Create('Hans','Hansvej 1'));
     lines.Add(TLine.Create('Jens','Jensvej 1'));
     lines.Add(TLine.Create('Frederik','Frederikvej 1'));
     lines.Add(TLine.Create('Jonas','Jonasvej 1'));

如今让咱们把lines对象列表与Edit1,Edit2可视控件进行绑定:

var
  bnd:IkbmMWBinding;
begin
     Binding.Clear;
     bnd:=Binding.Bind(lines,'Name',Edit1,'Text');
     Binding.Bind(lines,'Address',Edit2,'Text');
     if bnd.Navigator<>nil then
        bnd.Navigator.First;
end;

在这里,同时介绍了两个新内容:

  1. 使用Binding.Clear删除全部现有绑定
  2. 使用bnd.Navigator对列表进行导航

请记住,若是有全局变量引用IkbmMWBinding,则必须在调用Binding.Clear以前将它们设置为nil 若是不这样处理,特别是在将数据集做为数据源时(见后文),kbmMW将没法可靠地跟踪绑定之间的共享数据集。

调用Binding.Bind都会返回一个IkbmMWBinding接口,该接口可用于操做该特定绑定。IkbmMWBinding提供一个有用的属性是IkbmMWBindingNavigator类型的Navigator,经过Navigator能够轻松访问可导航的数据源...例如,象lines这样的列表对象。若是绑定的数据源不是列表,则Navigator为nil。

如今,你能够用bnd.Navigator.First/Last/Next/Previous进行导航,同时获取返回的书签。

对于特定来源的全部绑定,Navigator是常见且单一的。不一样的可导航数据源具备本身的Navigator实例。

绑定数据集(TDataSet)

SmartBinding实现了可视控件与TDataSet子类的绑定,只要是TDataSet的子类,均可以绑定。如今,让咱们经过将biolife.csv加载到TkbmMemTable中来制做数据源。

var
   mt:TkbmMemTable;
   csv:TkbmCSVStreamFormat;
begin
     csv:=TkbmCSVStreamFormat.Create(nil);
     try
        mt:=TkbmMemTable.Create(nil);
        mt.LoadFromFileViaFormat('biolife.csv',csv);
     finally
        csv.Free;
     end;

接下来,让咱们对Edit控件进行绑定:

     Binding.Clear;
     bnd:=Binding.Bind(mt,'Category',Edit1,'Text',[mwboTwoWay]);
     Binding.Bind(mt,'Species Name',Edit2,'Text',[mwboTwoWay]);
     if bnd.Navigator<>nil then
        bnd.Navigator.First;

与绑定对象列表同样,彻底相同的绑定方式。在本例中,使用了双向绑定方式,即mwboTwoWay方式,目的是使Edit1和Edit2与Delphi的数据敏感控件TDBEdit控件的行为相同,能够利用导航器(bnd.Navigator),轻松导航数据集。

如今,在Edit1中输入内容,会自动保存到Category字段中,当mt的当前记录发生变化时,Edit1会显示当前记录的内容。另外,当用代码修改数据集的内容时,一样也会显示到对应有Edit控件中。如执行下面代码:

mt.Edit;
mt.FieldByName('Category').AsString:='测试!!!';
mt.Post;

Edit1的内容显示为测试!!!

绑定ListBox或ComboBox

有时候想要使用数据源列表/数据集(或其选定部分)来填充TListTCombobox(或其子类)。

对于此示例,咱们还但愿同步控件,即在其中一个控件中进行的选择会自动反映在另外一个控件中。

     Binding.Clear;
     Binding.Bind(mt,'Species Name',ComboBox1,'Items');
     Binding.Bind(mt,'@',ComboBox1,'ItemIndex',[mwboTwoWay]);
     bnd:=Binding.Bind(mt,'Common_Name',ListBox2,'Items');
     Binding.Bind(mt,'@',ListBox2,'ItemIndex',[mwboTwoWay]);
     if bnd.Navigator<>nil then
        bnd.Navigator.First;

首先,咱们将数据集mt中的Species Name字段绑定TCombobox的Items属性,而后将另外一个字段Common_Name绑定到TListBox的Items,以后,再使用@做为数据源属性,表示kbmMW SmartBinding引用数据源列表或者数据集的位置。这里,咱们将数据源的位置绑定到TListBoxTComboboxItemIndex(位置)此外,咱们告诉SmartBinding用两种方式(mwboTwoWay),当更改数据源导航器中的位置不只会更新TListBoxTCombobox中的位置,同时在控件中选择某些内容也会自动更新源列表/数据集位置,在这种状况下,确保两个控件彼此自动同步确保其来源一致。

绑定到Grid

看下面的代码,咱们将数据集mt与Grid的指定列绑定,用@将数据集当前记录位置与Grid的当前行位置绑定,另外还用@将数据集位置与Grid的第一列绑定,同时,还将TEdit绑定到Grid的RowCount属性,因此能在运行期显示变化的Grid行数,即数据集的的记录数。

     Binding.Clear;
     bnd:=Binding.Bind(mt,'Category',StringGrid1,'#1');
     Binding.Bind(mt,'Species Name',StringGrid1,'#2');
     Binding.Bind(mt,'@',StringGrid1,'@',[mwboTwoWay]);//将数据源与控件的位置进行双向绑定 // 在Grid的第一列显示位置数
     Binding.Bind(mt,'@',StringGrid1,'#0');

     // Bind to rowcount for easy on the fly change at runtime
     Binding.Bind(leRowCount,'Text',StringGrid1,'RowCount');

     if bnd.Navigator<>nil then
        bnd.Navigator.First;

 它仍然与咱们习惯的绑定方式相同,但如今使用了#n语法,将数据集的字段绑定到Grid中的指定列(以0开头)。

使用导航器,Grid如今就像TDBGrid同样。由于咱们将数据集的@与Grid的的@绑定到一块儿了,因此当滚动源数据集,Grid自动更新当前行位置,更改Grid当前选定行也将自动更新数据集的当前记录位置,数据集中当前记录的变化将自动反映在网格中,而且咱们双向绑定方式,输入到网格中当前行的数据将反映回数据源。

2019-05-22 后记:笔者基于5.09版本作测试,用上面绑定的方法,在Grid中修改的结果,不会自动更新回数据集,若是用下面一行代码绑定:

bnd:=Binding.Bind(dataset,'Category',StringGrid1,'#1',[mwboTwoWay]);

则Grid中第一行显示不正常,去掉[mwboTwoWay],则正常显示。以下图:

第一行第一个字段没有显示。

绑定匿名函数

不只对象、数据或数据集能够充当绑定的数据源或目标,匿名函数也能够。

     // Show calling function when Edit1.Text is changed.
     Binding.Bind(Edit1,'Text',function(const AProxy:TkbmMWBindingCustomProxy; var AValue:TValue):boolean
                               begin
                                   Log.Debug('Got change from binding to Edit1: '+AValue.ToString);
                                   Result:=true;
                               end);

上面的示例基本上能够理解为将匿名函数做为Edit1.OnChange事件处理程序,Edit1.Text的变化将被检测到,自动执行匿名函数。在这个例子中,它使用kbmMW日志框架TkbmMWLog记录变化状况。

下面示例是如何使用匿名函数做为数据源:

     Binding.Bind(function(const AProxy:TkbmMWBindingCustomProxy; var AValue:TValue):boolean
                  begin
                       AValue:=Random(100);
                       Result:=true;
                  end,Edit1,'Text');

如今重复调用匿名函数。每次函数返回一个新值时,Edit1.Text都会更新。轮询函数的频率取决于咱们用于定义绑定的Binding实例的属性UpdateFrequency的设置。默认频率是每秒10次,但您能够随时更改频率以符合您的喜爱。

     Binding.UpdateFrequency:=1000;

扩充绑定

若是要定义从一个源获取数值的绑定,并将其输出到标签但格式不一样,该怎么办?

您将使用Binding.Bind函数返回的结果接口上可用ToDestinationExpression方法

     // Show calling function to populate Edit1.Text and format its look.
     Binding.Bind(function(const AProxy:TkbmMWBindingCustomProxy; var AValue:TValue):boolean
                  begin
                       AValue:=Random(100);
                       Result:=true;
                  end,Edit1,'Text')
            .ToDestinationExpression('"Hello "+data');

上面的代码,咱们简单地用前面的函数绑定示例,并要求kbmMW SmartBinding根据ToDestinationExpression函数中给出的字符串表达式在目标路径上增长数据此示例致使Edit1.Text包含值'Hello'和随机数。

由于字符串表达式基于kbmMW在其余地方利用的相同表达式处理功能,因此表达式很是丰富。这些功能源自具备kbmMemTables功能的SQL解析器和评估程序。在这种状况下,咱们只支持像表达式部分这样的数学,而不是SQL自己。可是你可使用你指望可以使用的全部常规操做,包括许多不错的转换,正则表达式,数学,条件评估和更多功能。

因为绑定能够是双向的,所以还须要可以在返回数据源时格式化或者可能格式化的值。为此,SmartBinding也提供了ToSourceExpression函数。

     Binding.Bind(Edit1,'Text',Edit2,'Text',[mwboTwoWay])
            .ToDestinationExpression('"Hello "+data')
            .ToSourceExpression('Mid(data,7)');

这个例子抓住Edit1.Text中的内容并将其放入Edit2.Text中,文本“Hello”为前缀。可是,它还能够识别Edit2.Text中所作的更改,在首先删除它的前6个字符后将该文本移动到Edit1.Text这种类型的双向绑定一般会使事件驱动的绑定变得疯狂,由于TEdit控件中的更改可能会发生无限的事件触发可是,kbmMW SmartBinding不受这些事件的影响,并确保以最小的代价进行更新。

启用,禁用,解除绑定和从新绑定

有时您可能但愿阻止绑定来完成其工做。若是预防只是暂时的,那么一种方法是禁用它。

var
   bnd:IkbmMWBinding;
begin
   bnd:=Binding.Bind(....);
...
   bnd.Disable:=true;
...
   bnd.Disable:=false;
end;

若是您想要永久禁用它,您也能够删除它。为此目的,存在Unbind方法。

Binding.UnbindSource(Edit1);

以上将取消绑定Edit1做为任何绑定的数据源。

Binding.UnbindDestination(Edit2);

以上将取消绑定Edit2做为任何绑定的目标。

您还可使用调用Bind方法时返回IkbmMWBinding取消绑定

var
  MyBinding:IkbmMWBinding;
begin
  MyBinding:=Binding.Bind(....);
...
  Binding.Unbind(MyBinding);

若是您未绑定到匿名函数,则还可使用与绑定彻底相同的参数取消绑定

Binding.Bind(Edit1,'Text',Edit2,'Text');
...
Binding.Unbind(Edit1,'Text',Edit2,'Text');

最后你可能想要从新绑定。Rebind基本上能够修改从一个源或目标实例到另外一个源或目标实例的绑定。绑定到瞬态记录或对象时特别有趣

Binding.Rebind(@data,@data2);

以上更改了引用记录或内存缓冲区“ 数据 ”的任何绑定,并更新这些绑定以引用记录或内存缓冲区“ data2 ”。

一样,您能够从新绑定控件

Binding.Rebind(Edit1,NewEdit1);

引用Edit1的全部绑定如今将引用NewEdit1

序幕

您可能已经注意到,运行时绑定的语法是一致且简单的,而且在重构用户界面或控件时能够轻松地重构绑定。

我以前提到过,除了现有的线程安全Binding单例以外,您还能够选择建立本身的绑定管理器实例。这样作的缘由可能包括您但愿不一样的绑定在某种缘由的不一样时间间隔更新,或者您但愿很是容易地访问丢弃或从新建立全部绑定,例如一个简单的框架中的框架,而不会影响定义的全部其余绑定在其余框架中,无需明确解除其中的每个。

var
  myBindingMgr:TkbmMWBindings;
begin
  myBindingMgr:=TkbmMWBindings.Create(1000);
...
  myBindingMgr.Free;

上面的示例建立了另外一个绑定管理器,它只会每秒轮询一次。当你再也不须要它们时,请记得释放本身建立的绑定管理器。

我脑子里还有不少关于使绑定更容易并添加更多功能的想法,但这将是下一个完整版的kbmMW企业版中包含的beta代码。

若是您喜欢咱们的产品和帖子,请与您认识的全部人分享这些帖子!

kbmMW用于简化软件开发的编码,让人专一于业务功能而不是基础代码。为何?由于我讨厌在开发最终用户代码时作基础工做。因此实际上并无为你开发全部这些东西,而是为了我本身使用,这很自私与自我,但但愿你也会喜欢它。

哦..那个特点图片的含义是什么?
嗯..它能够解释不少方式......这里有一些

  • Smartbinding为那些从活动的悬崖上掉下来的人们进行救援
  • 绑定时,您须要确保绑定(系绳)是安全的,不会致使危险的问题
  • 作出本身的解释 

https://components4developers.blog/2019/04/25/smartbinding-with-kbmmw-1/

2019-05-22 译者注:当前5.09版本已经发布,但SmartBinding不支持FMX ListView控件,这让我感到很是遗憾,由于个人app使用了大量的ListView。急盼做者能考虑在下一版本中实现。

相关文章
相关标签/搜索