SmartBinding與kbmMW#2

前言

在以前的文章中,我介紹了SmartBinding做爲Delphi的一個新的易於使用和智能的綁定框架。介紹了包括綁定對象,列表,常規數據和可視控件,以及如何使用導航器,全部這些都用代碼作了演示。html

本文將重點關注下一個kbmMW版本中包含的新SmartBinding功能(SmartBinding v2),預計很快就會發布。數據庫

一行代碼綁定

爲使kbmMW SmartBind更加智能,目的之一就是要刪除全部重複的綁定代碼,使開發者只關注具體的功能需求。經過代碼執行SmartBinding很是簡單,但爲了更容易實現,請看下面的演示:
express

 

先作一個簡單的Form,如上圖,Form包含一個TLabel,一個TButton和兩個TEdit。數組

試着運行它:服務器

當在Edit3輸入內容時,其他控件的Caption和Text都會自動更新。這個功能我已經在以前的SmartBinding博文中介紹過,不知你還記得不?框架

在以前的博文中,有許多Binding.Bind 代碼行將控件綁定在一塊兒,但在此例子中,只須要在TForm的OnCreate事件中用一行綁定代碼:dom

     Binding.AutoBind(self);

怎麼可能?!如今的kbmMW SmartBinding可以檢查對象和控件的String屬性,以獲取綁定指令。編碼

Label.Caption屬性中能夠清楚地看到一個綁定指令:{Edit3.Text}spa

只需將綁定內容放在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的要求,只是由於大多數狀況下的綁定定義將遵循特定控件,只要在其中一個控件中定義綁定便可字符串屬性。在其餘控件(可視或非可見)字符串屬性中定義綁定的狀況下,您至少不會經過簡單地重構控件來鬆散綁定定義。

它能夠輕鬆訪問加載和保存文件的綁定,以處理很是晚的綁定,這能夠在安裝時用於很是動態的應用程序客戶專業化,而不是編譯。

相關文章
相關標籤/搜索