即將發布的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也會更新。
Binding實例來自哪里?他是kbmMW SmartBinding默認帶的一個實例,可以立即使用。代碼的最后一行還包括一個可選參數mwboTwoWay,表示綁定是同步的,更改一方也會自動更新另一方。
基本上所有字符串,布爾值,浮點,int64和整數屬性都可以通過自動轉換輕松綁定,其中SmartBinding確保根據需要自動在不同類型之間轉換數據。其他類型的數據也可以綁定,但源和目標屬性必須是相同的類型(也有方法也可以...見后面)。
線程安全方面是什么情況呢?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屬性中。
線程安全是什么情況?
上面的例子顯然有些簡單。但是,由於兩個綁定都引用了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;
在這里,同時介紹了兩個新內容:
- 使用Binding.Clear刪除所有現有綁定
- 使用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
有時候想要使用數據源列表/數據集(或其選定部分)來填充TList或TCombobox(或其子類)。
對於此示例,我們還希望同步控件,即在其中一個控件中進行的選擇會自動反映在另一個控件中。
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引用數據源列表或者數據集的位置。這里,我們將數據源的位置綁定到TListBox和TCombobox的ItemIndex(位置)。此外,我們告訴SmartBinding用兩種方式(mwboTwoWay),當更改數據源導航器中的位置不僅會更新TListBox和TCombobox中的位置,同時在控件中選擇某些內容也會自動更新源列表/數據集位置,在這種情況下,確保兩個控件彼此自動同步確保其來源一致。
綁定到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。急盼作者能考慮在下一版本中實現。
后記:在5.10.20版本中,作者完美實現了!