前言:
Delphi FireMonkey 的 LiveBindings 可以用来直接绑定界面元素和一个对象的属性,这样就可以把对象的值显示到界面上。常见的是将数据库 DataSet 的数据显示到界面上。
如果数据是一组对象,放到一个 TObjectList 里面,该如何做?
以下操作在 Delphi 10.4 社区版上测试通过。
正文开始:
1. 有一个对象:
TMyDev = class
private
FDevID: string;
FDevName: string;
FIsOnline: Integer;
FIsSelected: Integer;
public
property DevID: string read FDevID write FDevID;
property DevName: string read FDevName write FDevName;
property IsOnline: Integer read FIsOnline write FIsOnline;
property IsSelected: Integer read FIsSelected write FIsSelected;
end;
2. 有一个存储对象的列表:
FDevList: TObjectList<TMyDev>;
3. 拖一个 PrototypeBindSource1 到 Form1 上面。双击它,弹出字段编辑器窗口。
3.1. 字段编辑器窗口左上角有个 add new 的图标,或者右键点击字段编辑器窗口出下拉菜单选择 add,添加一个字段。
3.2. 字段编辑器窗口里面,选择添加的字段,给它的 Name 属性设置名字,给 FieldType 属性设置这个字段的数据类型。这里要注意,字段名字要和该字段对应的对象的属性名字完全一样。
3. 拖一个 ListView 到 Form1 上面。右键点击下拉菜单,选择 Bind Visually 菜单,IDE 的底部出现可视化绑定的界面,在这个界面里面拉线实现绑定。
3.1. 把 ListView1 的 Synch 拉线连接 PrototypeBindSource1 的星号(*)上面。
3.2. 把 ListView1 的 ItemText 拉线连接 PrototypeBindSource1 的 DevID 字段;
4. 选择界面上的 PrototypeBindSource1,在 IDE 的属性窗口的事件页,看到 PrototypeBindSource1 只有一个事件,双击这个事件产生代码框架,代码如下:
procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
//PrototypeBindSource1 的事件,在这里指定它的 ABindSourceAdapter 是一个 TListBindSourceAdapter
// 这个 TListBindSourceAdapter 对应一个 TObjectList
ABindSourceAdapter := TListBindSourceAdapter<TMyDev>.Create(Self, FDevList, True);
end;
5. 上述 4 这个事件早于 FormCreate 事件,而这里又用到 FDevList 对象。因此,需要在 FormCreate 之前就要创建 FDevList 对象。因此,这里可以有两个办法:
5.1. constructor Create(AOwner: TComponent); override; 覆盖掉 Form 的 Create 方法,在这个方法里面创建 FDevList。
constructor TForm1.Create(AOwner: TComponent);
begin
FDevList := TObjectList<TMyDev>.Create;
inherited;
end;
5.2. 直接在前述事件里面创建:
procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
FDevList := TObjectList<TMyDev>.Create;
ABindSourceAdapter := TListBindSourceAdapter<TMyDev>.Create(Self, FDevList, True);
end;
6. 运行期增加的对象的数据,显示到 ListView1 里面:
procedure TForm1.BtnAddClick(Sender: TObject);
var
ADev: TMyDev;
begin
ADev := TMyDev.Create;
ADev.DevID := 'ID-' + Self.FCount.ToString;
ADev.DevName := 'DevName-' + Self.FCount.ToString;
ADev.IsOnline := 0;
ADev.IsSelected := 0;
FDevList.Add(ADev);
//必须调用 Refresh 否则 ListView1 里面不会显示新增加的对象。
PrototypeBindSource1.Refresh;
Inc(Self.FCount);
end;
以下是完整的代码:
unit Unit1;
{
测试 LiveBindings 一个 TObjectList,显示多个对象内容到 ListView 或者 ListBox
}
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
Data.Bind.Components, Data.Bind.ObjectScope, FMX.TabControl, System.Generics.Collections,
FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base,
System.Rtti, System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.EngExt,
Fmx.Bind.DBEngExt, FMX.Controls.Presentation, FMX.StdCtrls, FMX.ListView;
type
TMyDev = class
private
FDevID: string;
FDevName: string;
FIsOnline: Integer;
FIsSelected: Integer;
public
property DevID: string read FDevID write FDevID;
property DevName: string read FDevName write FDevName;
property IsOnline: Integer read FIsOnline write FIsOnline;
property IsSelected: Integer read FIsSelected write FIsSelected;
end;
TForm1 = class(TForm)
PrototypeBindSource1: TPrototypeBindSource;
TabControl1: TTabControl;
TabItem1: TTabItem;
TabItem2: TTabItem;
ListView1: TListView;
Panel1: TPanel;
BindingsList1: TBindingsList;
LinkListControlToField1: TLinkListControlToField;
BtnActive: TButton;
Label1: TLabel;
BtnAdd: TButton;
procedure PrototypeBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
procedure FormCreate(Sender: TObject);
procedure BtnAddClick(Sender: TObject);
procedure BtnActiveClick(Sender: TObject);
private
{ Private declarations }
FCount: Integer;
FDevList: TObjectList<TMyDev>;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.BtnActiveClick(Sender: TObject);
begin
PrototypeBindSource1.Active := True;
end;
procedure TForm1.BtnAddClick(Sender: TObject);
var
ADev: TMyDev;
begin
ADev := TMyDev.Create;
ADev.DevID := 'ID-' + Self.FCount.ToString;
ADev.DevName := 'DevName-' + Self.FCount.ToString;
ADev.IsOnline := 0;
ADev.IsSelected := 0;
FDevList.Add(ADev);
//必须调用 Refresh 否则 ListView1 里面不会显示新增加的对象。
PrototypeBindSource1.Refresh;
Inc(Self.FCount);
end;
constructor TForm1.Create(AOwner: TComponent);
var
ADev: TMyDev;
begin
{
FDevList := TObjectList<TMyDev>.Create;
ADev := TMyDev.Create;
ADev.DevID := 'ID-1';
ADev.DevName := 'DevName-1';
ADev.IsOnline := 0;
ADev.IsSelected := 0;
FDevList.Add(ADev);
ADev := TMyDev.Create;
ADev.DevID := 'ID-2';
ADev.DevName := 'DevName-2';
ADev.IsOnline := 1;
ADev.IsSelected := 0;
FDevList.Add(ADev);
ADev := TMyDev.Create;
ADev.DevID := 'ID-3';
ADev.DevName := 'DevName-3';
ADev.IsOnline := 0;
ADev.IsSelected := 0;
FDevList.Add(ADev);
ADev := TMyDev.Create;
ADev.DevID := 'ID-4';
ADev.DevName := 'DevName-4';
ADev.IsOnline := 0;
ADev.IsSelected := 0;
FDevList.Add(ADev);
}
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Label1.Text := 'FormCreate';
FCount := 0;
end;
procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
//PrototypeBindSource1 的事件,在这里指定它的 ABindSourceAdapter 是一个 TListBindSourceAdapter
// 这个 TListBindSourceAdapter 对应一个 TObjectList
FDevList := TObjectList<TMyDev>.Create;
ABindSourceAdapter := TListBindSourceAdapter<TMyDev>.Create(Self, FDevList, True);
end;
end.