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。急盼做者能考慮在下一版本中實現。

相關文章
相關標籤/搜索