這是《一步一步學習使用LiveBindings(8)》的接續,本章將學習如下知識點:
- 為TGrid應用列格式。
- 創建自定義的綁定方法。
- 實現表單級別的格式化方法。
上一節使用嚮導將TGrid綁定到ProtoTypeBindSource,它會創建TLinkGridToDataSource這個綁定鏈接。嚮導創建的綁定只是將PrototypeBindSource1的*與TGrid的*進行了綁定,這種綁定模式在運行時會自動創建Column,所以設計時沒有辦法去操控列。
綁定的正確方式應該是在PrototypeBindSource1的Field與TGrid的Column進行綁定,
1. Grid的列格式化
如果需要對單獨的列進行操作,可以使用兩種方法:
- 選中LinkGridToDataSourcePrototypeBindSource1,然後在屬性編輯器中選擇“Columns”屬性,單擊右側的按鈕,打開列編輯器,在這裏手動添加新的列,並設置DataMember為相應的字段,或者單擊“Add All Fields”按鈕,將一次性添加所有的字段。

- 在LiveBindings Designer中將Field直接拖動到TGrid上,完成綁定操作。

在完成後,在LiveBindings Designer選中任意一條連接線,屬性面板上均只顯示LinkGridToDataSourcePrototypeBindSource1這個綁定鏈接,因此如果要添加公式,需要在屬性面板中,打開Columns編輯器,選中某一個具體的Column進行設置。
每一個Column具有如下的一些屬性:
- ColumnStyle : 默認情況下,列被創建為 TStringColumn 。但是,您可能希望選擇一個與其內容相關的更好值(數字、日期值等)。
- CustomFormat : 此屬性允許您指定用於在網格中渲染值的表達式。
- CustomParse : 此屬性允許您指定用於將用户輸入的值轉換為列的適當值的表達式。
- Header : 該屬性設置列的標題。
- MemberName : 該屬性設置列的對應字段。
- ReadOnly : 該屬性定義列是否可以被用户編輯。
- Visible : 該屬性設置列的可見性(你可以創建列,然後決定每列的可見性)。
- Width : 該屬性指定列的寬度。
CustomFormat和CustomParse格式表達式,與本系列在《一步一步學習使用LiveBindings(8)》中介紹的使用方式完全相同。
下圖是接下來實現的效果:

這裏為HireDate列應用了日期格式化,
CustomFormat:
FormatDateTime('MMMM yyyy',self.Value)
Salary列應用了貨幣格式化。
CustomFormat:
Format('%%m', self.Value + 0.0)
CustomParse:
SubString(%s, 1, 15)
在Grid最後添加了2列,一列是薪資等級,如果薪資高的,就加了一個綠色向上的手勢,如果薪資不是那麼高的,就加了一個綠色的手勢。看上去就顯得專業多了。
要實現這樣的目的,在這裏添加了2個新的列,一個列的ColumnStyle指定為GlyphColumn,另一個列指定為ProgressColumn列類型。
注意:雖然屬性編輯器中,ColumnStyle下拉框並沒有提供一個ProgressColumn類型,但仍然可以通過輸入ProgressColumn來顯示進度條。
GlyphColumn可以顯示來自TImageList控件中的圖片,因此在主窗體上添加了一個TImageList控件,將TGrid的Images屬性指向它。
在這個ImageList控件中,僅放了2張圖片,分別是2個手勢,因此其ImageIndex分別為0和1。在CustomFormat中,寫下如下的公式:
IfThen(Self.Value>60000, 0, 1)
如果年薪大於6萬,則認為是高薪了,打個綠色的手勢,否則顯示紅色的手勢。
ProgressColumn將使用表達式給它一個當前最高薪資的比率。如果是Manager,則最高薪資為20萬,否則最高薪資設為10萬。公式如下:
100*(Self.Value /IfThen(Owner.Title.Value='Manager', 200000, 100000) )
可以看到在表達式中又套用了表達式,這個可以靈活應用。
2. 自定義綁定方法
貨幣格式目前的解析公式不能滿足需求,SubString(%s, 1, 15)雖然簡單的移除了前綴的貨幣符號,但是對於數字中的分隔符號就不能識別,因此會發生錯誤。系統內置的方法並沒有好的解決方案,好在還可以創建自定義的綁定方法。
請按下面的步驟實現自定義的綁定方法:
1. 單擊主菜單中的 File > New > Package ,創建一個新的Delphi包。
建議立即單擊工具欄上的Save All按鈕,將包保存為CurrencyBindingMethod.dproj。
然後在包中添加一個新的Unit,保存為Methods.FormatCurrency.pas。
創建自定義綁定方法需要調用System.Bindings.Methods單元中的MakeInvokable方法,它返回定義在System.Bindings.EvalProtocol單元中的IInvokable接口,需要在uses區中添加對這2個單元的引用。
完整代碼如下所示,請參考代碼步驟:
unit Methods.FormatCurrency;
interface
uses
SysUtils;
implementation
uses
//MakeInvokable 函數、 TMethodDescription 類型定義和 TBindingsMethodFactory 單例
System.Bindings.Methods,
//IValue,TValueWrapper 和 IInvokable 類型定義
System.Bindings.EvalProtocol;
//將貨幣類型專換為字符串類型
function CurrencyToStrInvokable: IInvokable;
begin
Result := MakeInvokable(
function (Args: TArray<IValue>): IValue
var
AValue:Currency;
CurrencyStr:string;
begin
// 1 - 輸入參數識別並進行驗證
if Length(Args) < 1 then
raise EEvaluatorError.Create('CurrencyToStr: 至少需要一個參數');
// 2 - 調用真實的轉換CurrToStrF方法
AValue:=Args[0].GetValue.AsCurrency;
CurrencyStr:= CurrToStrF(AValue, ffCurrency, 2);
// 3 - 將結果值打包為TValueWrapper
Exit(TValueWrapper.Create(CurrencyStr));
end
);
end;
//將字符串類型專換為貨幣類型。
function StrToCurrencyInvokable: IInvokable;
begin
Result := MakeInvokable(
function (Args: TArray<IValue>): IValue
var
AValue:String;
CurrencyVal:Currency;
C: char;
LDigits: string;
begin
// 1 - 輸入參數識別並進行驗證
if Length(Args) < 1 then
raise EEvaluatorError.Create('StrToCurrency: 至少需要一個參數');
// 2 - 將參數轉換為真實的參數類型,再進行實際的業務處理
AValue:=Args[0].GetValue.AsString;
for C in AValue do
case C of
'0'..'9',
'.':
LDigits := LDigits + C;
end;
CurrencyVal := StrToCurr(LDigits);
// 3 - 將結果裝包裝起來
Exit(TValueWrapper.Create(CurrencyVal));
end
);
end;
initialization
// 4 - 註冊方法
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
CurrencyToStrInvokable
, 'CurrencyToStr', 'CurrencyToStr', '', True
, '貨幣轉換為字符串'
, nil
)
);
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
StrToCurrencyInvokable
, 'StrToCurrency', 'StrToCurrency', '', True
, '字符串轉換為貨幣'
, nil
)
);
finalization
// 5 - 卸載已經註冊的方法
TBindingMethodsFactory.UnRegisterMethod('CurrencyToStr');
TBindingMethodsFactory.UnRegisterMethod('StrToCurrency');
end.
可以看到,代碼中定義了CurrencyToStr和StrToCurrency這兩個方法,它們都接收一個參數,並且調用了SysUtil中的CurrToStrF和StrToCurr函數進行了轉換。
2. 在Project Manager窗口右擊CurrencyBindingMethod項目名稱,先Build一次,再單擊"Install"菜單項進行安裝。

安裝完成後,回到LiveBindings_BindFormat.dproj項目,選中TBindingList控件,在屬性編輯器中找到Methods屬性,單擊右側的按鈕,可以看到CurrencyToStr和StrToCurrency這兩個方法已經出現在了列表中。

3. 要讓我們自己的項目能夠正確識別這2個方法,還需要將包中的Methods.FormatCurrency.pas單元添加到uMainForm.pas的uses區,否則會提示方法無法找到的錯誤。
請在Project > Options > Delphi Compiler > Search Path路徑中添加了對Package代碼的引用。

在uses區中添加如下的引用:
implementation
{$R *.fmx}
uses
System.Bindings.Helper, System.Bindings.Methods,
System.Bindings.EvalProtocol,Methods.FormatCurrency;
將Salary的公式改為如下:
CustomFormat:
CurrencyToStr(self.Value)
CustomParse
StrToCurrency(%s)
經過這樣的處理,現在在貨幣的顯示和輸入上,就非常理想了,如下圖所示:

4. 使用自定義的格式化方法
在《一步一步學習使用LiveBindings(8)》中曾經介紹過通過self.owner.owner,返回到了主窗體TMainForm,我們在主窗體上定義了一個屬性MyProgName,可以通過self.owner.owner.MyProgName訪問到此屬性。同理,還可以在主窗體上定義一個函數,通過self.owner.owner進行訪問,實現自定義的格式化方法。
實現過程如下:
1. 首先在uMainForm的public區定義了一個函數,實現根據指定的格式化字符串格式化數字,定義與實現如下所示:
public
{ Public declarations }
property MyProgName:string read GetProgName;
//根據指定的格式化字符串或區域格式化符點數。
function FormFormatFloat(const AFormat: string; const AValue: Extended; const ALocale: string): string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.fmx}
uses
System.Bindings.Helper, System.Bindings.Methods,
System.Bindings.EvalProtocol,Methods.FormatCurrency;
function TfrmMain.FormFormatFloat(const AFormat: string; const AValue: Extended;
const ALocale: string): string;
var
LFormatSettings: TFormatSettings;
begin
LFormatSettings := FormatSettings;
if not ALocale.IsEmpty then
LFormatSettings := TFormatSettings.Create(ALocale);
Result := FormatFloat(AFormat, AValue, LFormatSettings);
end;
接下來,我使用LiveBindings Wizard嚮導新建了一個TEdit指向Salary的鏈接,然後為這個TEdit的綁定鏈接指定CustomFormat如下,使其保留4位小數:
Self.Owner.Owner.FormFormatFloat('#,#0.0000', self.Value, '')
效果如下:

可以看到,果然正確的實現了格式化顯示。
右邊的那個手勢圖標,是一個TGlyph組件,它可以連接到一個TImageList控件,通過在LiveBindings Designer中,將Salary綁定到其ImageIndex屬性。最後應用瞭如下的公式:
IfThen(Self.Value>60000, 0, 1)

總結
本文介紹了格式化的幾種進階方法:
- 使用表達式列格式化。
- 自定義綁定方法。
- 使用自定義表單方法格式化。
強烈推薦使用自定義綁定方法,不是很推薦自定義表單方法,但有時候表單級別的方法能解決很多業務邏輯相關的格式化問題。所以具體如何架構,還是要具體問題,具體分析。