一步一步学习使用LiveBindings() 使用TAdapterBindSource实现对象绑定
一步一步学习使用LiveBindings使用TAdapterBindSource实现对象绑定LiveBindings 是 RAD StudioDelphi/CBuilder中一套强大的数据绑定框架它允许你在 UI 控件与数据源之间建立声明式连接无需编写繁琐的事件处理代码。而TAdapterBindSource是其中极为关键的一环——它充当“适配器”将任意对象如类实例、记录、甚至嵌套属性转换为 LiveBindings 可识别的数据流。本文将从原理出发通过可运行的示例带你逐步掌握TAdapterBindSource的使用。### 为什么需要 TAdapterBindSource在 LiveBindings 生态中TBindSourceDB用于绑定数据集如TFDQuery而TAdapterBindSource则用于绑定非数据集对象。它的核心价值在于-统一接口将普通对象的属性暴露为字段供TBindingsList或TBindScope使用。-双向绑定支持从 UI 写回对象属性自动处理类型转换。-动态字段可在设计期或运行期定义字段映射。TAdapterBindSource内部使用TAdapter模式它维护一个内部列表FAdapter每个元素对应一个对象实例而字段则映射到对象的属性通过 RTTI 或手动指定。### 环境准备- Delphi 10.3 或 CBuilder以下示例以 Delphi 为例- 创建一个 VCL 或 FMX 应用放置以下控件 -TAdapterBindSource组件面板 → Data Binding -TBindingsList组件面板 → Data Binding - 两个TEdit输入姓名、年龄 - 一个TButton触发写入### 第一步定义对象类型我们创建一个简单的TEmployee类包含Name和Age属性。注意属性必须声明为 published以便 RTTI 访问。pascaltype TEmployee class private FName: string; FAge: Integer; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; end;### 第二步设计期配置 TAdapterBindSource1. 双击TAdapterBindSource打开“AdapterBindSource 编辑器”。2. 在“Fields”页签点击“Add Field”两次分别设置 - FieldName NameDataType string- FieldName AgeDataType Integer3. 关闭编辑器。此时AdapterBindSource1已定义了两个逻辑字段。### 第三步建立 UI 与字段的绑定在TBindingsList中创建两条绑定表达式-Edit1.Text←→AdapterBindSource1.Name-Edit2.Text←→AdapterBindSource1.Age操作方式在设计器中选择TBindingsList右键 → “Binding 编辑器”添加两条绑定。### 第四步运行期绑定对象实例现在我们需要将实际的TEmployee对象“挂载”到AdapterBindSource上。核心方法是AdapterBindSource1.AddObject或设置AdapterBindSource1.AutoEdit等属性。以下代码展示了完整流程pascalprocedure TForm1.FormCreate(Sender: TObject);var Emp: TEmployee;begin // 创建对象实例 Emp : TEmployee.Create; Emp.Name : 张三; Emp.Age : 30; // 将对象添加到 AdapterBindSource AdapterBindSource1.AddObject(Emp); // 激活绑定 AdapterBindSource1.Active : True; // 此时 Edit1.Text 自动变为 张三Edit2.Text 变为 30end;注意AddObject会将对象加入内部列表但AdapterBindSource默认只显示当前记录。若需管理多个对象可结合Navigate方法。### 第五步从 UI 写回对象属性LiveBindings 支持双向同步。当用户修改Edit1.Text时需要触发写入。最简单的方式是设置TBindingsList的NotifyUpdates为True或使用TEdit.OnChange事件。pascalprocedure TForm1.Edit1Change(Sender: TObject);begin // 强制将 UI 值同步到对象 BindingsList1.Notify(Sender, Edit1);end;或者使用TAdapterBindSource的Edit和Post方法pascalprocedure TForm1.Button1Click(Sender: TObject);begin // 开始编辑当前对象 AdapterBindSource1.Edit; // 更新字段值通过绑定自动完成但也可手动赋值 AdapterBindSource1.CurrentFields[Name].Value : Edit1.Text; AdapterBindSource1.CurrentFields[Age].Value : StrToInt(Edit2.Text); // 提交修改 AdapterBindSource1.Post;end;实际上如果绑定表达式正确Edit1.Text和Edit2.Text的变化会自动反映到对象中无需手动干预。但为了演示“写回”原理上述代码展示了底层操作。### 进阶动态绑定与嵌套属性TAdapterBindSource不仅支持简单属性还能绑定嵌套对象。例如假设TEmployee有一个Department属性其类型为TDepartment而TDepartment有DeptName属性。我们可以这样定义字段pascaltype TDepartment class private FDeptName: string; published property DeptName: string read FDeptName write FDeptName; end; TEmployee class private FName: string; FAge: Integer; FDept: TDepartment; public constructor Create; destructor Destroy; override; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; property Dept: TDepartment read FDept write FDept; end;在AdapterBindSource的字段编辑器中添加字段时路径可写为Dept.DeptNameLiveBindings 会自动解析嵌套属性。### 运行期添加字段高级技巧有时设计期无法预知所有字段我们可以在运行期动态创建字段pascalprocedure TForm1.DynamicAddField;var AField: TAdapterBindSourceField;begin AField : AdapterBindSource1.AddField(DeptName, TFieldType.ftString); // 将字段映射到对象的属性路径通过 OnMapObjectToField 事件 AdapterBindSource1.OnMapObjectToField : DoMapObjectToField;end;procedure TForm1.DoMapObjectToField(ASender: TObject; AObject: TObject; AField: TAdapterBindSourceField; var AValue: TValue; var AHandled: Boolean);begin if AField.FieldName DeptName then begin AValue : (AObject as TEmployee).Dept.DeptName; AHandled : True; end;end;### 完整可运行示例下面是一个综合示例演示了从创建对象、绑定、修改到写回的全过程。将以下代码放入一个 FMX 表单中放置两个TEdit、一个TLabel和一个TButton。pascalunit Unit1;interfaceuses System.SysUtils, System.Types, System.UITypes, System.Rtti, FMX.Types, FMX.Controls, FMX.Forms, FMX.Edit, FMX.StdCtrls, Data.Bind.Components, Data.Bind.ObjectScope;type TEmployee class private FName: string; FAge: Integer; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; end; TForm1 class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; Label1: TLabel; AdapterBindSource1: TAdapterBindSource; BindingsList1: TBindingsList; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private Emp: TEmployee; public end;var Form1: TForm1;implementation{$R *.fmx}procedure TForm1.FormCreate(Sender: TObject);begin // 创建员工对象 Emp : TEmployee.Create; Emp.Name : 李四; Emp.Age : 25; // 添加对象到 AdapterBindSource AdapterBindSource1.AddObject(Emp); AdapterBindSource1.Active : True; // 设计期已设置绑定Edit1.Text - NameEdit2.Text - Age // 此时界面自动显示李四和25end;procedure TForm1.Button1Click(Sender: TObject);begin // 修改对象属性 Emp.Name : 王五; Emp.Age : 35; // 通知绑定系统刷新 UI BindingsList1.Notify(Sender, Emp); // 或者使用 AdapterBindSource1.Refresh 强制刷新 // AdapterBindSource1.Refresh;end;end.### 性能与注意事项-TAdapterBindSource基于 RTTI因此对象必须编译为published属性且开启{$RTTI EXPLICIT METHODS([]) PROPERTIES([vcPublished])}或默认设置。- 频繁调用Notify或Refresh可能影响性能建议在批量更新时暂停绑定BindingsList1.BeginUpdate/EndUpdate。- 对于大型数据集考虑使用TPrototypeBindSource或直接绑定TFDMemTable以获得更好性能。### 总结TAdapterBindSource是 LiveBindings 中连接对象世界与 UI 世界的桥梁。通过它你可以- 将任意对象属性暴露为可绑定的字段。- 实现 UI 与对象属性的自动双向同步。- 动态扩展字段甚至绑定嵌套对象。掌握这一工具意味着你可以告别传统的手动赋值和事件处理以声明式编程的方式构建更清晰、更易维护的界面逻辑。从本文的示例出发你可以进一步探索TAdapterBindSource与TList、TObjectList的配合实现列表导航以及利用OnBeforeExecuteCommand等事件实现更复杂的业务逻辑。LiveBindings 的学习曲线略陡但其带来的生产力提升是值得投入的。