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)
private
FName: String;
FAge: string;
FMobile: string;
FStartDate: string;
FPosition: String;
public
constructor 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);
begin
inherited Create;
FName := AName;
FAge := AAge;
FMobile := AMobile;
FStartDate := AStartDate;
FPosition := APosition;
end;
6、解析JSON数据,转为TList<TEmployee>
procedure TForm1.LoadEmployListDataFromJson(AJsonStr: string);
var
i: Integer;
ja: TJsonArray;
jo: TJSONObject;
lEmployee: TEmployee;
begin
ja := TJSONObject.ParseJSONValue(AJsonStr) as TJSONArray;
try
// 注意:这里 EmployeeList 不用释放,因为它会添加到TListBindSourceAdapter,并释放
EmployeeList := TList<TEmployee>.Create;
for i := 0 to ja.Count-1 do
begin
jo := 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;
finally
ja.Free;
end;
end;
7、通过AdapterBindSource1的onCreateAdapter事件为TDataGeneratorAdapter添加TList<TEmployee>数据
procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
LoadEmployListDataFromJson(employeeJson);
// 将 EmployeeList 数据绑定到 DataGeneratorAdapter1
// 其中 Create 方法的参数 AOwnsObject 默认为 true,控件释放时,会释放 EmployeeList 对象
ABindSourceAdapter := TListBindSourceAdapter<TEmployee>.Create(Self, EmployeeList);
end;
三、代码实现
完整代码如下:
unit Unit1;
interface
uses
System.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;
const
employeeJson = '[{"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"}]';
type
TEmployee = class(TObject)
private
FName: String;
FAge: string;
FMobile: string;
FStartDate: string;
FPosition: String;
public
constructor 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;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TEmployee }
constructor TEmployee.Create(const AName: String; const AAge: string;
const AMobile: string; const AStartDate: string; const APosition: String);
begin
inherited Create;
FName := AName;
FAge := AAge;
FMobile := AMobile;
FStartDate := AStartDate;
FPosition := APosition;
end;
{ TForm1 }
procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
LoadEmployListDataFromJson(employeeJson);
// 将 EmployeeList 数据绑定到 DataGeneratorAdapter1
// 其中 Create 方法的参数 AOwnsObject 默认为 true,控件释放时,会释放 EmployeeList 对象
ABindSourceAdapter := TListBindSourceAdapter<TEmployee>.Create(Self, EmployeeList);
end;
procedure TForm1.LoadEmployListDataFromJson(AJsonStr: string);
var
i: Integer;
ja: TJsonArray;
jo: TJSONObject;
lEmployee: TEmployee;
begin
ja := TJSONObject.ParseJSONValue(AJsonStr) as TJSONArray;
try
// 注意:这里 EmployeeList 不用释放,因为它会添加到TListBindSourceAdapter,并释放
EmployeeList := TList<TEmployee>.Create;
for i := 0 to ja.Count-1 do
begin
jo := 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;
finally
ja.Free;
end;
end;
end.
窗体文件:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 451
ClientWidth = 582
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
DesignerMasterStyle = 0
object StringGrid1: TStringGrid
CanFocus = True
ClipChildren = True
Position.X = 64.000000000000000000
Position.Y = 144.000000000000000000
Size.Width = 425.000000000000000000
Size.Height = 241.000000000000000000
Size.PlatformDefault = False
TabOrder = 3
RowCount = 200
Viewport.Width = 421.000000000000000000
Viewport.Height = 216.000000000000000000
end
object BindNavigator1: TBindNavigator
Position.X = 64.000000000000000000
Position.Y = 393.000000000000000000
Size.Width = 425.000000000000000000
Size.Height = 41.000000000000000000
Size.PlatformDefault = False
TabOrder = 4
DataSource = AdapterBindSource1
xRadius = 4.000000000000000000
yRadius = 4.000000000000000000
end
object Text1: TText
Position.X = 64.000000000000000000
Position.Y = 40.000000000000000000
Size.Width = 425.000000000000000000
Size.Height = 73.000000000000000000
Size.PlatformDefault = False
Text = #29992'LiveBindings'#23454#29616#32465#23450'JSON'#25968#25454#21040'Grid'
TextSettings.Font.Size = 18.000000000000000000
TextSettings.FontColor = claCoral
end
object BindingsList1: TBindingsList
Methods = <>
OutputConverters = <>
Left = 112
Top = 232
object LinkGridToDataSourceAdapterBindSource1: TLinkGridToDataSource
Category = 'Quick Bindings'
DataSource = AdapterBindSource1
GridControl = StringGrid1
Columns = <>
end
end
object AdapterBindSource1: TAdapterBindSource
AutoActivate = True
OnCreateAdapter = AdapterBindSource1CreateAdapter
Adapter = DataGeneratorAdapter1
ScopeMappings = <>
Left = 240
Top = 232
end
object DataGeneratorAdapter1: TDataGeneratorAdapter
FieldDefs = <>
Active = True
AutoPost = False
Options = [loptAllowInsert, loptAllowDelete, loptAllowModify]
Left = 392
Top = 232
end
end