Delphi 利用LiveBindings绑定JSON数据到列表控件
Delphi通过HTTP接口从远程服务器获取JSON数据,并显示在TStringGrid控件上。
一、LiveBindings技术简介
为了提供数据绑定方案,Delphi FMX 框架提供了 LiveBindings 的技术。
LiveBindings 基本上是一种基于表达式的机制,其中通常有一个源组件和一个目标组件,目的是将一个或多个涉及源成员的表达式绑定到目标的一个或多个属性。为了实现这一点,系统需要在某个地方存储涉及组件(在表单、框架或数据模块中)的绑定表达式集合(TBindingList 组件)。为了使数据对表达式引擎可用,需要使用中间组件(BindSources)在表达式引擎和实际数据存储之间充当中介。
其中BindSources组件包含TBindSourceDB、TBindSourceDBX、TPrototypeBindSource、TAdapterBindSource。
TBindSourceDB或TBindSourceDBX的DataSet属性可以连接TFDMemTable等数据库控件;
TAdapterBindSource的Adapter属性连接TDataGeneratorAdapter控件,继而绑定TList<T>数据集。
二、解决方案
以Delphi 12.1作为开发工具,用网格控件TStringGrid显示员工信息的JSON数据,JSON数据如下:
[{"name":"宋江",
"age":29,
"mobile":"13812345678",
"startDate":"2023-09-11",
"position":"developer"
},
{"name":"卢俊义",
"age":29,
"mobile":"13812345678",
"startDate":"2023-09-11",
"position":"developer"
},
{"name":"吴用",
"age":29,
"mobile":"13812345678",
"startDate":"2023-09-11",
"position":"developer"
},
{"name":"林冲",
"age":29,
"mobile":"13812345678",
"startDate":"2023-09-11",
"position":"developer"
}]
1、窗体上添加 TBindingsList、 TAdapterBindSource、TDataGeneratorAdapter、TBindNavigator、TStringGrid组件
2、双击BindingsList1,添加TBindGridLink类型的数据绑定表达式,源组件选择AdapterBindSource1,目标组件选择StringGrid1
3、将AdapterBindSource1的Adapter设置为 DataGeneratorAdapter1
4、将BindNavigator1的DataSource设置为AdapterBindSource1
5、新建TEmployee类
TEmployee = class(TObject)privateFName: String;FAge: string;FMobile: string;FStartDate: string;FPosition: String;publicconstructor Create(const AName: String; const AAge: string; const AMobile: string;const AStartDate: string; const APosition: String); overload;property Name: String read FName write FName;property Age: string read FAge write FAge;property Mobile: String read FMobile write FMobile;property StartDate: string read FStartDate write FStartDate;property Position: String read FPosition write FPosition;end;{ TEmployee }constructor TEmployee.Create(const AName: String; const AAge: string;const AMobile: string; const AStartDate: string; const APosition: String);
begininherited Create;FName := AName;FAge := AAge;FMobile := AMobile;FStartDate := AStartDate;FPosition := APosition;
end;
6、解析JSON数据,转为TList<TEmployee>
procedure TForm1.LoadEmployListDataFromJson(AJsonStr: string);
vari: Integer;ja: TJsonArray;jo: TJSONObject;lEmployee: TEmployee;
beginja := TJSONObject.ParseJSONValue(AJsonStr) as TJSONArray;try// 注意:这里 EmployeeList 不用释放,因为它会添加到TListBindSourceAdapter,并释放EmployeeList := TList<TEmployee>.Create;for i := 0 to ja.Count-1 dobeginjo := ja.Items[i] as TJSONObject;lEmployee := TEmployee.Create(jo.GetValue('name').ToString, jo.GetValue('age').ToString,jo.GetValue('mobile').ToString, jo.GetValue('startDate').ToString, jo.GetValue('position').ToString);EmployeeList.Add(lEmployee);end;finallyja.Free;end;
end;
7、通过AdapterBindSource1的onCreateAdapter事件为TDataGeneratorAdapter添加TList<TEmployee>数据
procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;var ABindSourceAdapter: TBindSourceAdapter);
beginLoadEmployListDataFromJson(employeeJson);// 将 EmployeeList 数据绑定到 DataGeneratorAdapter1// 其中 Create 方法的参数 AOwnsObject 默认为 true,控件释放时,会释放 EmployeeList 对象ABindSourceAdapter := TListBindSourceAdapter<TEmployee>.Create(Self, EmployeeList);
end;
三、代码实现
完整代码如下:
unit Unit1;interfaceusesSystem.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,Data.Bind.EngExt, Fmx.Bind.DBEngExt, Data.Bind.GenData, System.Rtti,FMX.Grid.Style, Data.Bind.Controls, System.Bindings.Outputs, Fmx.Bind.Editors,Data.Bind.Components, FMX.Layouts, Fmx.Bind.Navigator,FMX.Controls.Presentation, FMX.ScrollBox, FMX.Grid, Data.Bind.ObjectScope,System.Generics.Collections, System.JSON, Fmx.Bind.Grid, Data.Bind.Grid,FMX.Objects;constemployeeJson = '[{"name":"宋江","age":29,"mobile":"13812345678","startDate":"2023-09-11","position":"developer"},'+ '{"name":"卢俊义","age":29,"mobile":"13812345678","startDate":"2023-09-11","position":"developer"},'+ '{"name":"吴用","age":29,"mobile":"13812345678","startDate":"2023-09-11","position":"developer"}, '+ '{"name":"林冲","age":29,"mobile":"13812345678","startDate":"2023-09-11","position":"developer"}]';typeTEmployee = class(TObject)privateFName: String;FAge: string;FMobile: string;FStartDate: string;FPosition: String;publicconstructor Create(const AName: String; const AAge: string; const AMobile: string;const AStartDate: string; const APosition: String); overload;property Name: String read FName write FName;property Age: string read FAge write FAge;property Mobile: String read FMobile write FMobile;property StartDate: string read FStartDate write FStartDate;property Position: String read FPosition write FPosition;end;TForm1 = class(TForm)BindingsList1: TBindingsList;AdapterBindSource1: TAdapterBindSource;DataGeneratorAdapter1: TDataGeneratorAdapter;StringGrid1: TStringGrid;BindNavigator1: TBindNavigator;LinkGridToDataSourceAdapterBindSource1: TLinkGridToDataSource;Text1: TText;procedure AdapterBindSource1CreateAdapter(Sender: TObject;var ABindSourceAdapter: TBindSourceAdapter);private{ Private declarations }EmployeeList: TList<TEmployee>;procedure LoadEmployListDataFromJson(AJsonStr: string);public{ Public declarations }end;varForm1: TForm1;implementation{$R *.fmx}{ TEmployee }constructor TEmployee.Create(const AName: String; const AAge: string;const AMobile: string; const AStartDate: string; const APosition: String);
begininherited Create;FName := AName;FAge := AAge;FMobile := AMobile;FStartDate := AStartDate;FPosition := APosition;
end;{ TForm1 }procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;var ABindSourceAdapter: TBindSourceAdapter);
beginLoadEmployListDataFromJson(employeeJson);// 将 EmployeeList 数据绑定到 DataGeneratorAdapter1// 其中 Create 方法的参数 AOwnsObject 默认为 true,控件释放时,会释放 EmployeeList 对象ABindSourceAdapter := TListBindSourceAdapter<TEmployee>.Create(Self, EmployeeList);
end;procedure TForm1.LoadEmployListDataFromJson(AJsonStr: string);
vari: Integer;ja: TJsonArray;jo: TJSONObject;lEmployee: TEmployee;
beginja := TJSONObject.ParseJSONValue(AJsonStr) as TJSONArray;try// 注意:这里 EmployeeList 不用释放,因为它会添加到TListBindSourceAdapter,并释放EmployeeList := TList<TEmployee>.Create;for i := 0 to ja.Count-1 dobeginjo := ja.Items[i] as TJSONObject;lEmployee := TEmployee.Create(jo.GetValue('name').ToString, jo.GetValue('age').ToString,jo.GetValue('mobile').ToString, jo.GetValue('startDate').ToString, jo.GetValue('position').ToString);EmployeeList.Add(lEmployee);end;finallyja.Free;end;
end;end.
窗体文件:
object Form1: TForm1Left = 0Top = 0Caption = 'Form1'ClientHeight = 451ClientWidth = 582FormFactor.Width = 320FormFactor.Height = 480FormFactor.Devices = [Desktop]DesignerMasterStyle = 0object StringGrid1: TStringGridCanFocus = TrueClipChildren = TruePosition.X = 64.000000000000000000Position.Y = 144.000000000000000000Size.Width = 425.000000000000000000Size.Height = 241.000000000000000000Size.PlatformDefault = FalseTabOrder = 3RowCount = 200Viewport.Width = 421.000000000000000000Viewport.Height = 216.000000000000000000endobject BindNavigator1: TBindNavigatorPosition.X = 64.000000000000000000Position.Y = 393.000000000000000000Size.Width = 425.000000000000000000Size.Height = 41.000000000000000000Size.PlatformDefault = FalseTabOrder = 4DataSource = AdapterBindSource1xRadius = 4.000000000000000000yRadius = 4.000000000000000000endobject Text1: TTextPosition.X = 64.000000000000000000Position.Y = 40.000000000000000000Size.Width = 425.000000000000000000Size.Height = 73.000000000000000000Size.PlatformDefault = FalseText = #29992'LiveBindings'#23454#29616#32465#23450'JSON'#25968#25454#21040'Grid'TextSettings.Font.Size = 18.000000000000000000TextSettings.FontColor = claCoralendobject BindingsList1: TBindingsListMethods = <>OutputConverters = <>Left = 112Top = 232object LinkGridToDataSourceAdapterBindSource1: TLinkGridToDataSourceCategory = 'Quick Bindings'DataSource = AdapterBindSource1GridControl = StringGrid1Columns = <>endendobject AdapterBindSource1: TAdapterBindSourceAutoActivate = TrueOnCreateAdapter = AdapterBindSource1CreateAdapterAdapter = DataGeneratorAdapter1ScopeMappings = <>Left = 240Top = 232endobject DataGeneratorAdapter1: TDataGeneratorAdapterFieldDefs = <>Active = TrueAutoPost = FalseOptions = [loptAllowInsert, loptAllowDelete, loptAllowModify]Left = 392Top = 232end
end